Sunhey Cho commited on
Commit
08c2fcd
Β·
1 Parent(s): 16e07bc

Frontend build copied to static, add 5 test images, update API integration; ignore static backup folders

Browse files
Files changed (45) hide show
  1. api.py +173 -1
  2. frontend/src/App.js +21 -3
  3. frontend/src/components/ProductComparison.js +647 -0
  4. product_comparison.py +1108 -0
  5. static/asset-manifest.json +11 -11
  6. static/index.html +1 -1
  7. static/precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js +0 -30
  8. static/{precache-manifest.2fad43b74e0c6c48ddb79498a374f3f4.js β†’ precache-manifest.653a7aee73b10eaaa2fae0610652f6fa.js} +8 -8
  9. static/precache-manifest.90eb425b86312bf2b8dbe43e7d0377d8.js +0 -30
  10. static/precache-manifest.9a82b4d1673393a138e3168d2b6c841d.js +0 -30
  11. static/service-worker.js +2 -2
  12. static/static/css/main.3b4eede1.chunk.css.map +0 -1
  13. static/static/css/{main.3b4eede1.chunk.css β†’ main.59c2a54e.chunk.css} +2 -2
  14. static/static/css/main.59c2a54e.chunk.css.map +1 -0
  15. static/static/js/{2.9b5b80e8.chunk.js β†’ 2.abf91741.chunk.js} +0 -0
  16. static/static/js/{2.9b5b80e8.chunk.js.LICENSE.txt β†’ 2.abf91741.chunk.js.LICENSE.txt} +0 -0
  17. static/static/js/{2.9b5b80e8.chunk.js.map β†’ 2.abf91741.chunk.js.map} +0 -0
  18. static/static/js/main.975ea04a.chunk.js +0 -2
  19. static/static/js/main.975ea04a.chunk.js.map +0 -1
  20. static/static/js/main.db88b5b1.chunk.js +2 -0
  21. static/static/js/main.db88b5b1.chunk.js.map +1 -0
  22. static/static/js/main.fb4939ae.chunk.js +0 -2
  23. static/static/js/main.fb4939ae.chunk.js.map +0 -1
  24. static/static/js/main.fc0e2dc7.chunk.js +0 -2
  25. static/static/js/main.fc0e2dc7.chunk.js.map +0 -1
  26. static/static/js/main.fe247ab2.chunk.js +0 -2
  27. static/static/js/main.fe247ab2.chunk.js.map +0 -1
  28. static_bak_20250820_085943/asset-manifest.json +0 -24
  29. static_bak_20250820_085943/index.html +0 -1
  30. static_bak_20250820_085943/manifest.json +0 -15
  31. static_bak_20250820_085943/precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js +0 -30
  32. static_bak_20250820_085943/service-worker.js +0 -39
  33. static_bak_20250820_085943/static/css/main.3b4eede1.chunk.css +0 -2
  34. static_bak_20250820_085943/static/css/main.3b4eede1.chunk.css.map +0 -1
  35. static_bak_20250820_085943/static/js/2.9b5b80e8.chunk.js +0 -0
  36. static_bak_20250820_085943/static/js/2.9b5b80e8.chunk.js.LICENSE.txt +0 -58
  37. static_bak_20250820_085943/static/js/2.9b5b80e8.chunk.js.map +0 -0
  38. static_bak_20250820_085943/static/js/3.9013e23f.chunk.js +0 -2
  39. static_bak_20250820_085943/static/js/3.9013e23f.chunk.js.map +0 -1
  40. static_bak_20250820_085943/static/js/main.fc0e2dc7.chunk.js +0 -2
  41. static_bak_20250820_085943/static/js/main.fc0e2dc7.chunk.js.map +0 -1
  42. static_bak_20250820_085943/static/js/runtime-main.25710301.js +0 -2
  43. static_bak_20250820_085943/static/js/runtime-main.25710301.js.map +0 -1
  44. test_images/README.txt +22 -0
  45. test_product_comparison_api.py +151 -0
api.py CHANGED
@@ -3,7 +3,7 @@
3
  import os
4
  os.environ['MPLCONFIGDIR'] = '/tmp/matplotlib'
5
 
6
- from flask import Flask, request, jsonify, send_from_directory, redirect, url_for, session, render_template_string, make_response
7
  from datetime import timedelta
8
  import torch
9
  from PIL import Image
@@ -19,6 +19,8 @@ from flask_cors import CORS
19
  import json
20
  import sys
21
  import requests
 
 
22
  try:
23
  from openai import OpenAI
24
  except Exception as _e:
@@ -54,6 +56,14 @@ import chromadb
54
  from chromadb.utils import embedding_functions
55
 
56
  app = Flask(__name__, static_folder='static')
 
 
 
 
 
 
 
 
57
  # ν™˜κ²½ λ³€μˆ˜μ—μ„œ λΉ„λ°€ ν‚€λ₯Ό κ°€μ Έμ˜€κ±°λ‚˜, μ—†μœΌλ©΄ μ•ˆμ „ν•œ 랜덀 ν‚€ 생성
58
  secret_key = os.environ.get('FLASK_SECRET_KEY')
59
  if not secret_key:
@@ -895,6 +905,168 @@ def add_detected_objects():
895
  print(f"Error processing object: {e}")
896
  continue
897
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
898
  # 객체가 μ—†λŠ” 경우
899
  if not object_ids:
900
  return jsonify({"error": "No valid objects to add"})
 
3
  import os
4
  os.environ['MPLCONFIGDIR'] = '/tmp/matplotlib'
5
 
6
+ from flask import Flask, request, jsonify, send_from_directory, redirect, url_for, session, render_template_string, make_response, Response, stream_with_context
7
  from datetime import timedelta
8
  import torch
9
  from PIL import Image
 
19
  import json
20
  import sys
21
  import requests
22
+ import asyncio
23
+ from threading import Thread
24
  try:
25
  from openai import OpenAI
26
  except Exception as _e:
 
56
  from chromadb.utils import embedding_functions
57
 
58
  app = Flask(__name__, static_folder='static')
59
+
60
+ # Import product comparison coordinator
61
+ try:
62
+ from product_comparison import get_product_comparison_coordinator, decode_base64_image
63
+ except ImportError:
64
+ print("Warning: Product comparison module not available")
65
+ get_product_comparison_coordinator = None
66
+ decode_base64_image = None
67
  # ν™˜κ²½ λ³€μˆ˜μ—μ„œ λΉ„λ°€ ν‚€λ₯Ό κ°€μ Έμ˜€κ±°λ‚˜, μ—†μœΌλ©΄ μ•ˆμ „ν•œ 랜덀 ν‚€ 생성
68
  secret_key = os.environ.get('FLASK_SECRET_KEY')
69
  if not secret_key:
 
905
  print(f"Error processing object: {e}")
906
  continue
907
 
908
+ # Add to vector DB
909
+ if object_ids and object_embeddings and object_metadatas:
910
+ object_collection.add(
911
+ ids=object_ids,
912
+ embeddings=object_embeddings,
913
+ metadatas=object_metadatas
914
+ )
915
+
916
+ return jsonify({
917
+ "success": True,
918
+ "message": f"Added {len(object_ids)} objects to collection",
919
+ "object_ids": object_ids
920
+ })
921
+ else:
922
+ return jsonify({
923
+ "warning": "No valid objects to add"
924
+ })
925
+
926
+ except Exception as e:
927
+ print(f"Error in add-detected-objects API: {e}")
928
+ return jsonify({"error": str(e)}), 500
929
+
930
+
931
+ # Product Comparison API Endpoints
932
+ @app.route('/api/product/compare/start', methods=['POST'])
933
+ @login_required
934
+ def start_product_comparison():
935
+ """Start a new product comparison session"""
936
+ if get_product_comparison_coordinator is None:
937
+ return jsonify({"error": "Product comparison module not available"}), 500
938
+
939
+ try:
940
+ # Generate session ID if provided in form or query params, otherwise create new one
941
+ session_id = request.form.get('session_id') or request.args.get('session_id') or str(uuid.uuid4())
942
+
943
+ # Get analysis type if provided (info, compare, value, recommend)
944
+ analysis_type = request.form.get('analysisType') or request.args.get('analysisType', 'info')
945
+
946
+ # Process images from FormData or JSON
947
+ images = []
948
+
949
+ # Check if request is multipart form data
950
+ if request.files:
951
+ # Handle FormData with file uploads (from frontend)
952
+ if 'image1' in request.files and request.files['image1']:
953
+ img1 = request.files['image1']
954
+ try:
955
+ images.append(Image.open(img1.stream))
956
+ except Exception as e:
957
+ print(f"Error processing image1: {e}")
958
+
959
+ if 'image2' in request.files and request.files['image2']:
960
+ img2 = request.files['image2']
961
+ try:
962
+ images.append(Image.open(img2.stream))
963
+ except Exception as e:
964
+ print(f"Error processing image2: {e}")
965
+
966
+ # Fallback to JSON with base64 images (for API testing)
967
+ elif request.json and 'images' in request.json:
968
+ image_data_list = request.json.get('images', [])
969
+ for image_data in image_data_list:
970
+ img = decode_base64_image(image_data)
971
+ if img is not None:
972
+ images.append(img)
973
+
974
+ if not images:
975
+ return jsonify({"error": "No valid images provided"}), 400
976
+
977
+ # Get coordinator instance
978
+ coordinator = get_product_comparison_coordinator()
979
+
980
+ # Pass the analysis type and session metadata to the coordinator
981
+ session_metadata = {
982
+ 'analysis_type': analysis_type,
983
+ 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S')
984
+ }
985
+
986
+ # Start processing in a background thread
987
+ def run_async_task(loop):
988
+ asyncio.set_event_loop(loop)
989
+ loop.run_until_complete(coordinator.process_images(session_id, images, session_metadata))
990
+
991
+ loop = asyncio.new_event_loop()
992
+ thread = Thread(target=run_async_task, args=(loop,))
993
+ thread.daemon = True
994
+ thread.start()
995
+
996
+ # Return session ID for client to use with streaming endpoint
997
+ return jsonify({
998
+ "session_id": session_id,
999
+ "message": "Product comparison started",
1000
+ "status": "processing"
1001
+ })
1002
+
1003
+ except Exception as e:
1004
+ print(f"Error starting product comparison: {e}")
1005
+ return jsonify({"error": str(e)}), 500
1006
+
1007
+
1008
+ @app.route('/api/product/compare/stream/<session_id>', methods=['GET'])
1009
+ @login_required
1010
+ def stream_product_comparison(session_id):
1011
+ """Stream updates from a product comparison session"""
1012
+ if get_product_comparison_coordinator is None:
1013
+ return jsonify({"error": "Product comparison module not available"}), 500
1014
+
1015
+ def generate():
1016
+ """Generate SSE events for streaming"""
1017
+ coordinator = get_product_comparison_coordinator()
1018
+ last_message_index = 0
1019
+ retry_count = 0
1020
+ max_retries = 300 # 5 minutes at 1 second intervals
1021
+
1022
+ while retry_count < max_retries:
1023
+ # Get current status
1024
+ status = coordinator.get_session_status(session_id)
1025
+ if status is None:
1026
+ # Session not found
1027
+ yield f"data: {json.dumps({'error': 'Session not found'})}\n\n"
1028
+ break
1029
+
1030
+ # Get all messages
1031
+ messages = coordinator.get_session_messages(session_id)
1032
+
1033
+ # Send any new messages
1034
+ if messages and len(messages) > last_message_index:
1035
+ new_messages = messages[last_message_index:]
1036
+ for msg in new_messages:
1037
+ yield f"data: {json.dumps({'message': msg})}\n\n"
1038
+ last_message_index = len(messages)
1039
+
1040
+ # Send current status
1041
+ yield f"data: {json.dumps({'status': status})}\n\n"
1042
+
1043
+ # If completed or error, send final result and end stream
1044
+ if status in ['completed', 'error']:
1045
+ result = coordinator.get_session_result(session_id)
1046
+ yield f"data: {json.dumps({'final_result': result})}\n\n"
1047
+ break
1048
+
1049
+ # Wait before next update
1050
+ time.sleep(1)
1051
+ retry_count += 1
1052
+
1053
+ # End the stream if we've reached max retries
1054
+ if retry_count >= max_retries:
1055
+ yield f"data: {json.dumps({'error': 'Timeout waiting for results'})}\n\n"
1056
+
1057
+ return Response(
1058
+ stream_with_context(generate()),
1059
+ mimetype="text/event-stream",
1060
+ headers={
1061
+ 'Cache-Control': 'no-cache',
1062
+ 'X-Accel-Buffering': 'no',
1063
+ 'Content-Type': 'text/event-stream',
1064
+ }
1065
+ )
1066
+ except Exception as e:
1067
+ print(f"Error processing object: {e}")
1068
+ continue
1069
+
1070
  # 객체가 μ—†λŠ” 경우
1071
  if not object_ids:
1072
  return jsonify({"error": "No valid objects to add"})
frontend/src/App.js CHANGED
@@ -9,13 +9,16 @@ import {
9
  AppBar,
10
  Toolbar,
11
  ThemeProvider,
12
- createMuiTheme
 
 
13
  } from '@material-ui/core';
14
  import ImageUploader from './components/ImageUploader';
15
  import ModelSelector from './components/ModelSelector';
16
  import ResultDisplay from './components/ResultDisplay';
17
  import LlmAnalysis from './components/LlmAnalysis';
18
  import OpenAIChat from './components/OpenAIChat';
 
19
  import './App.css';
20
 
21
  // Create a theme
@@ -45,6 +48,7 @@ function App() {
45
  detr: false,
46
  vit: false
47
  });
 
48
 
49
  // Check API status on component mount
50
  useEffect(() => {
@@ -140,12 +144,23 @@ function App() {
140
  <AppBar position="static">
141
  <Toolbar>
142
  <Typography variant="h6" style={{ flexGrow: 1 }}>
143
- Multi-Model Object Detection Demo
144
  </Typography>
145
  </Toolbar>
 
 
 
 
 
 
 
 
 
 
146
  </AppBar>
147
  <Container maxWidth="lg" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>
148
- <Grid container spacing={3}>
 
149
  <Grid item xs={12}>
150
  <Paper style={{ padding: theme.spacing(2) }}>
151
  <Typography variant="h5" gutterBottom>
@@ -212,6 +227,9 @@ function App() {
212
  <OpenAIChat imageBase64={imageBase64} />
213
  </Grid>
214
  </Grid>
 
 
 
215
  </Container>
216
  </Box>
217
  </ThemeProvider>
 
9
  AppBar,
10
  Toolbar,
11
  ThemeProvider,
12
+ createMuiTheme,
13
+ Tabs,
14
+ Tab
15
  } from '@material-ui/core';
16
  import ImageUploader from './components/ImageUploader';
17
  import ModelSelector from './components/ModelSelector';
18
  import ResultDisplay from './components/ResultDisplay';
19
  import LlmAnalysis from './components/LlmAnalysis';
20
  import OpenAIChat from './components/OpenAIChat';
21
+ import ProductComparison from './components/ProductComparison';
22
  import './App.css';
23
 
24
  // Create a theme
 
48
  detr: false,
49
  vit: false
50
  });
51
+ const [activeTab, setActiveTab] = useState(0);
52
 
53
  // Check API status on component mount
54
  useEffect(() => {
 
144
  <AppBar position="static">
145
  <Toolbar>
146
  <Typography variant="h6" style={{ flexGrow: 1 }}>
147
+ Vision LLM Multi-Agent System
148
  </Typography>
149
  </Toolbar>
150
+ <Tabs
151
+ value={activeTab}
152
+ onChange={(e, newValue) => setActiveTab(newValue)}
153
+ indicatorColor="secondary"
154
+ textColor="inherit"
155
+ centered
156
+ >
157
+ <Tab label="Object Detection" />
158
+ <Tab label="Product Comparison" />
159
+ </Tabs>
160
  </AppBar>
161
  <Container maxWidth="lg" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>
162
+ {activeTab === 0 ? (
163
+ <Grid container spacing={3}>
164
  <Grid item xs={12}>
165
  <Paper style={{ padding: theme.spacing(2) }}>
166
  <Typography variant="h5" gutterBottom>
 
227
  <OpenAIChat imageBase64={imageBase64} />
228
  </Grid>
229
  </Grid>
230
+ ) : (
231
+ <ProductComparison />
232
+ )}
233
  </Container>
234
  </Box>
235
  </ThemeProvider>
frontend/src/components/ProductComparison.js ADDED
@@ -0,0 +1,647 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import {
3
+ Box,
4
+ Paper,
5
+ Grid,
6
+ Typography,
7
+ Button,
8
+ CircularProgress,
9
+ Tabs,
10
+ Tab,
11
+ Divider,
12
+ Card,
13
+ CardContent,
14
+ CardMedia,
15
+ IconButton,
16
+ TextField
17
+ } from '@material-ui/core';
18
+ import { AddCircle, Delete, Compare, Search, Info } from '@material-ui/icons';
19
+ import { makeStyles } from '@material-ui/core/styles';
20
+
21
+ const useStyles = makeStyles((theme) => ({
22
+ root: {
23
+ marginTop: theme.spacing(3),
24
+ marginBottom: theme.spacing(3),
25
+ },
26
+ imageContainer: {
27
+ position: 'relative',
28
+ minHeight: '200px',
29
+ display: 'flex',
30
+ justifyContent: 'center',
31
+ alignItems: 'center',
32
+ border: '2px dashed #ccc',
33
+ borderRadius: '8px',
34
+ margin: theme.spacing(1),
35
+ padding: theme.spacing(1),
36
+ },
37
+ progressLog: {
38
+ marginTop: theme.spacing(2),
39
+ height: '200px',
40
+ overflowY: 'auto',
41
+ backgroundColor: '#f8f9fa',
42
+ padding: theme.spacing(1),
43
+ borderRadius: '4px',
44
+ fontFamily: 'monospace',
45
+ fontSize: '0.9rem',
46
+ },
47
+ logEntry: {
48
+ margin: '4px 0',
49
+ padding: '2px 5px',
50
+ borderLeft: '3px solid #ccc',
51
+ },
52
+ logEntryAgent: {
53
+ borderLeft: '3px solid #2196f3',
54
+ },
55
+ logEntrySystem: {
56
+ borderLeft: '3px solid #4caf50',
57
+ },
58
+ logEntryError: {
59
+ borderLeft: '3px solid #f44336',
60
+ },
61
+ logTime: {
62
+ color: '#666',
63
+ fontSize: '0.8rem',
64
+ marginRight: theme.spacing(1),
65
+ },
66
+ imagePreview: {
67
+ maxWidth: '100%',
68
+ maxHeight: '300px',
69
+ objectFit: 'contain',
70
+ },
71
+ uploadIcon: {
72
+ fontSize: '3rem',
73
+ color: '#aaa',
74
+ },
75
+ uploadInput: {
76
+ display: 'none',
77
+ },
78
+ deleteButton: {
79
+ position: 'absolute',
80
+ top: '8px',
81
+ right: '8px',
82
+ backgroundColor: 'rgba(255, 255, 255, 0.8)',
83
+ '&:hover': {
84
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
85
+ },
86
+ },
87
+ tabPanel: {
88
+ padding: theme.spacing(2),
89
+ },
90
+ resultCard: {
91
+ marginTop: theme.spacing(2),
92
+ marginBottom: theme.spacing(2),
93
+ },
94
+ comparisonTable: {
95
+ width: '100%',
96
+ borderCollapse: 'collapse',
97
+ '& th, & td': {
98
+ border: '1px solid #ddd',
99
+ padding: '8px',
100
+ textAlign: 'left',
101
+ },
102
+ '& th': {
103
+ backgroundColor: '#f2f2f2',
104
+ },
105
+ '& tr:nth-child(even)': {
106
+ backgroundColor: '#f9f9f9',
107
+ },
108
+ },
109
+ loadingContainer: {
110
+ display: 'flex',
111
+ flexDirection: 'column',
112
+ alignItems: 'center',
113
+ padding: theme.spacing(4),
114
+ },
115
+ highlight: {
116
+ backgroundColor: '#e3f2fd',
117
+ padding: theme.spacing(1),
118
+ borderRadius: '4px',
119
+ fontWeight: 'bold',
120
+ }
121
+ }));
122
+
123
+ // 뢄석 μœ ν˜• νƒ­ νŒ¨λ„ μ»΄ν¬λ„ŒνŠΈ
124
+ function TabPanel(props) {
125
+ const { children, value, index, ...other } = props;
126
+
127
+ return (
128
+ <div
129
+ role="tabpanel"
130
+ hidden={value !== index}
131
+ id={`analysis-tabpanel-${index}`}
132
+ aria-labelledby={`analysis-tab-${index}`}
133
+ {...other}
134
+ >
135
+ {value === index && (
136
+ <Box p={3}>
137
+ {children}
138
+ </Box>
139
+ )}
140
+ </div>
141
+ );
142
+ }
143
+
144
+ const ProductComparison = () => {
145
+ const classes = useStyles();
146
+ const [images, setImages] = useState([null, null]); // μ΅œλŒ€ 2개 이미지 μ €μž₯
147
+ const [imagePreviews, setImagePreviews] = useState([null, null]);
148
+ const [isProcessing, setIsProcessing] = useState(false);
149
+ const [analysisResults, setAnalysisResults] = useState(null);
150
+ const [error, setError] = useState(null);
151
+ const [tabValue, setTabValue] = useState(0);
152
+ const [progressLogs, setProgressLogs] = useState([]);
153
+ const logEndRef = useRef(null);
154
+
155
+ // νƒ­ λ³€κ²½ ν•Έλ“€λŸ¬
156
+ const handleTabChange = (event, newValue) => {
157
+ setTabValue(newValue);
158
+ };
159
+
160
+ // 이미지 μ—…λ‘œλ“œ ν•Έλ“€λŸ¬
161
+ const handleImageUpload = (event, index) => {
162
+ const file = event.target.files[0];
163
+
164
+ if (file) {
165
+ // 이미지 미리보기 생성
166
+ const reader = new FileReader();
167
+ reader.onload = (e) => {
168
+ const newPreviews = [...imagePreviews];
169
+ newPreviews[index] = e.target.result;
170
+ setImagePreviews(newPreviews);
171
+ };
172
+ reader.readAsDataURL(file);
173
+
174
+ // 이미지 파일 μƒνƒœ μ—…λ°μ΄νŠΈ
175
+ const newImages = [...images];
176
+ newImages[index] = file;
177
+ setImages(newImages);
178
+
179
+ // κ²°κ³Ό 및 였λ₯˜ μ΄ˆκΈ°ν™”
180
+ setAnalysisResults(null);
181
+ setError(null);
182
+ }
183
+ };
184
+
185
+ // 이미지 μ‚­μ œ ν•Έλ“€λŸ¬
186
+ const handleImageDelete = (index) => {
187
+ const newImages = [...images];
188
+ const newPreviews = [...imagePreviews];
189
+
190
+ newImages[index] = null;
191
+ newPreviews[index] = null;
192
+
193
+ setImages(newImages);
194
+ setImagePreviews(newPreviews);
195
+ setAnalysisResults(null);
196
+ };
197
+
198
+ // 둜그 μΆ”κ°€ ν•¨μˆ˜
199
+ const addLog = (message, type = 'info') => {
200
+ const now = new Date();
201
+ const timeStr = now.toLocaleTimeString();
202
+ const newLog = {
203
+ time: timeStr,
204
+ message,
205
+ type // 'info', 'agent', 'system', 'error'
206
+ };
207
+
208
+ setProgressLogs(logs => [...logs, newLog]);
209
+ };
210
+
211
+ // 둜그창 μžλ™ 슀크둀
212
+ useEffect(() => {
213
+ if (logEndRef.current) {
214
+ logEndRef.current.scrollIntoView({ behavior: 'smooth' });
215
+ }
216
+ }, [progressLogs]);
217
+
218
+ // SSE μ—°κ²° ν•¨μˆ˜
219
+ const connectToSSE = (sessionId) => {
220
+ const eventSource = new EventSource(`/api/product/compare/stream/${sessionId}`);
221
+
222
+ eventSource.onmessage = (event) => {
223
+ try {
224
+ const data = JSON.parse(event.data);
225
+ if (data.message) {
226
+ addLog(data.message, data.agent || 'info');
227
+ }
228
+ // μ΅œμ’… κ²°κ³Όκ°€ 였면 뢄석 κ²°κ³Ό μ—…λ°μ΄νŠΈ
229
+ if (data.final_result) {
230
+ setAnalysisResults(data.final_result);
231
+ setIsProcessing(false);
232
+ eventSource.close();
233
+ }
234
+ } catch (err) {
235
+ addLog(`Event processing error: ${err.message}`, 'error');
236
+ }
237
+ };
238
+
239
+ eventSource.onerror = (err) => {
240
+ addLog('Server connection lost. Please try again in a moment.', 'error');
241
+ eventSource.close();
242
+ setIsProcessing(false);
243
+ };
244
+
245
+ return eventSource;
246
+ };
247
+
248
+ // μ œν’ˆ 뢄석 처리 ν•Έλ“€λŸ¬
249
+ const handleAnalysis = async () => {
250
+ // μœ νš¨μ„± 검사: μ΅œμ†Œ 1개 μ΄μƒμ˜ 이미지가 μžˆμ–΄μ•Ό 함
251
+ if (!images[0] && !images[1]) {
252
+ setError('Please upload at least one product image for analysis');
253
+ return;
254
+ }
255
+
256
+ setIsProcessing(true);
257
+ setError(null);
258
+ setProgressLogs([]); // 둜그 μ΄ˆκΈ°ν™”
259
+ addLog('Starting product analysis...', 'system');
260
+
261
+ try {
262
+ const formData = new FormData();
263
+
264
+ // μ—…λ‘œλ“œλœ μ΄λ―Έμ§€λ§Œ FormData에 μΆ”κ°€
265
+ if (images[0]) {
266
+ formData.append('image1', images[0]);
267
+ addLog('Product A image uploaded.', 'info');
268
+ }
269
+ if (images[1]) {
270
+ formData.append('image2', images[1]);
271
+ addLog('Product B image uploaded.', 'info');
272
+ }
273
+
274
+ // 뢄석 νƒ€μž… μΆ”κ°€ (νƒ­ 인덱슀둜 ꡬ뢄)
275
+ const analysisTypes = ['info', 'compare', 'value', 'recommend'];
276
+ const analysisType = analysisTypes[tabValue];
277
+ formData.append('analysisType', analysisType);
278
+ addLog(`Analysis type: ${analysisType === 'info' ? 'Product Information' : analysisType === 'compare' ? 'Performance Comparison' : analysisType === 'value' ? 'Value Analysis' : 'Purchase Recommendation'}`, 'system');
279
+
280
+ // λ°±μ—”λ“œ API 호좜 (μ„Έμ…˜ μ‹œμž‘)
281
+ addLog('Initializing analysis session...', 'system');
282
+ const response = await fetch('/api/product/compare/start', {
283
+ method: 'POST',
284
+ body: formData,
285
+ });
286
+
287
+ if (!response.ok) {
288
+ throw new Error(`HTTP error! Status: ${response.status}`);
289
+ }
290
+
291
+ const data = await response.json();
292
+ const sessionId = data.session_id;
293
+
294
+ if (!sessionId) {
295
+ throw new Error('Failed to receive session ID');
296
+ }
297
+
298
+ addLog(`Analysis session started. (Session ID: ${sessionId.substring(0, 8)}...)`, 'system');
299
+ addLog('Agents are collaborating to analyze products. Please wait a moment...', 'system');
300
+
301
+ // SSE 슀트림 μ—°κ²°
302
+ const eventSource = connectToSSE(sessionId);
303
+
304
+ // μ»΄ν¬λ„ŒνŠΈ μ–Έλ§ˆμš΄νŠΈμ‹œ μ—°κ²° μ’…λ£Œ
305
+ return () => {
306
+ eventSource.close();
307
+ };
308
+
309
+ } catch (err) {
310
+ console.error('μ œν’ˆ 뢄석 였λ₯˜:', err);
311
+ addLog(`Error occurred: ${err.message}`, 'error');
312
+ setError(`Error during analysis: ${err.message}`);
313
+ setIsProcessing(false);
314
+ }
315
+ };
316
+
317
+ // 뢄석 κ²°κ³Ό λ Œλ”λ§ ν•¨μˆ˜
318
+ const renderAnalysisResults = () => {
319
+ if (!analysisResults) return null;
320
+
321
+ // 뢄석 μœ ν˜•μ— 따라 λ‹€λ₯Έ κ²°κ³Ό ν‘œμ‹œ
322
+ switch (tabValue) {
323
+ case 0: // μ œν’ˆ 정보 탐색
324
+ return (
325
+ <Card className={classes.resultCard}>
326
+ <CardContent>
327
+ <Typography variant="h6" gutterBottom>Product Information</Typography>
328
+ <Divider style={{ margin: '8px 0 16px' }} />
329
+
330
+ {analysisResults.productInfo && (
331
+ <div>
332
+ <Typography variant="subtitle1">
333
+ <strong>Product Name:</strong> {analysisResults.productInfo.name}
334
+ </Typography>
335
+ <Typography variant="body1">
336
+ <strong>Brand:</strong> {analysisResults.productInfo.brand}
337
+ </Typography>
338
+ <Typography variant="body1">
339
+ <strong>Category:</strong> {analysisResults.productInfo.category}
340
+ </Typography>
341
+
342
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Key Specifications</Typography>
343
+ <ul>
344
+ {analysisResults.productInfo.specs.map((spec, index) => (
345
+ <li key={index}>
346
+ <Typography variant="body2">
347
+ <strong>{spec.name}:</strong> {spec.value}
348
+ </Typography>
349
+ </li>
350
+ ))}
351
+ </ul>
352
+
353
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Pros</Typography>
354
+ <ul>
355
+ {analysisResults.productInfo.pros.map((pro, index) => (
356
+ <li key={index}>
357
+ <Typography variant="body2">{pro}</Typography>
358
+ </li>
359
+ ))}
360
+ </ul>
361
+
362
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Cons</Typography>
363
+ <ul>
364
+ {analysisResults.productInfo.cons.map((con, index) => (
365
+ <li key={index}>
366
+ <Typography variant="body2">{con}</Typography>
367
+ </li>
368
+ ))}
369
+ </ul>
370
+ </div>
371
+ )}
372
+ </CardContent>
373
+ </Card>
374
+ );
375
+
376
+ case 1: // μ œν’ˆ μ„±λŠ₯ 비ꡐ
377
+ return (
378
+ <Card className={classes.resultCard}>
379
+ <CardContent>
380
+ <Typography variant="h6" gutterBottom>Product Comparison Analysis</Typography>
381
+ <Divider style={{ margin: '8px 0 16px' }} />
382
+
383
+ {analysisResults.comparison && (
384
+ <div>
385
+ <Typography variant="subtitle1" gutterBottom>Product Specification Comparison</Typography>
386
+
387
+ <table className={classes.comparisonTable}>
388
+ <thead>
389
+ <tr>
390
+ <th>Feature</th>
391
+ <th>Product A</th>
392
+ <th>Product B</th>
393
+ <th>Comparison</th>
394
+ </tr>
395
+ </thead>
396
+ <tbody>
397
+ {analysisResults.comparison.specs.map((spec, index) => (
398
+ <tr key={index}>
399
+ <td><strong>{spec.name}</strong></td>
400
+ <td>{spec.valueA}</td>
401
+ <td>{spec.valueB}</td>
402
+ <td>
403
+ <span className={spec.winner === 'A' ? classes.highlight : ''}>
404
+ {spec.comparison}
405
+ </span>
406
+ </td>
407
+ </tr>
408
+ ))}
409
+ </tbody>
410
+ </table>
411
+
412
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Overall Comparison</Typography>
413
+ <Typography variant="body1">{analysisResults.comparison.summary}</Typography>
414
+ </div>
415
+ )}
416
+ </CardContent>
417
+ </Card>
418
+ );
419
+
420
+ case 2: // 가격 λŒ€λΉ„ κ°€μΉ˜ 뢄석
421
+ return (
422
+ <Card className={classes.resultCard}>
423
+ <CardContent>
424
+ <Typography variant="h6" gutterBottom>Price-to-Value Analysis</Typography>
425
+ <Divider style={{ margin: '8px 0 16px' }} />
426
+
427
+ {analysisResults.valueAnalysis && (
428
+ <div>
429
+ <Typography variant="subtitle1">Price Information</Typography>
430
+ <Typography variant="body1">
431
+ <strong>Product A:</strong> {analysisResults.valueAnalysis.priceA}
432
+ </Typography>
433
+ {analysisResults.valueAnalysis.priceB && (
434
+ <Typography variant="body1">
435
+ <strong>Product B:</strong> {analysisResults.valueAnalysis.priceB}
436
+ </Typography>
437
+ )}
438
+
439
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Value Analysis</Typography>
440
+ <Typography variant="body1">{analysisResults.valueAnalysis.analysis}</Typography>
441
+
442
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Price-to-Performance Score</Typography>
443
+ <Typography variant="body1">
444
+ <strong>Product A:</strong> {analysisResults.valueAnalysis.valueScoreA}/10
445
+ </Typography>
446
+ {analysisResults.valueAnalysis.valueScoreB && (
447
+ <Typography variant="body1">
448
+ <strong>Product B:</strong> {analysisResults.valueAnalysis.valueScoreB}/10
449
+ </Typography>
450
+ )}
451
+ </div>
452
+ )}
453
+ </CardContent>
454
+ </Card>
455
+ );
456
+
457
+ case 3: // 졜적 ꡬ맀 μΆ”μ²œ
458
+ return (
459
+ <Card className={classes.resultCard}>
460
+ <CardContent>
461
+ <Typography variant="h6" gutterBottom>Purchase Recommendations</Typography>
462
+ <Divider style={{ margin: '8px 0 16px' }} />
463
+
464
+ {analysisResults.recommendation && (
465
+ <div>
466
+ <Typography variant="subtitle1" className={classes.highlight}>
467
+ Recommended Product: {analysisResults.recommendation.recommendedProduct}
468
+ </Typography>
469
+
470
+ <Typography variant="body1" style={{ marginTop: '16px' }}>
471
+ {analysisResults.recommendation.reason}
472
+ </Typography>
473
+
474
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Alternative Products</Typography>
475
+ <ul>
476
+ {analysisResults.recommendation.alternatives.map((alt, index) => (
477
+ <li key={index}>
478
+ <Typography variant="body2">
479
+ <strong>{alt.name}</strong>: {alt.reason}
480
+ </Typography>
481
+ </li>
482
+ ))}
483
+ </ul>
484
+
485
+ {analysisResults.recommendation.buyingTips && (
486
+ <>
487
+ <Typography variant="subtitle1" style={{ marginTop: '16px' }}>Buying Tips</Typography>
488
+ <ul>
489
+ {analysisResults.recommendation.buyingTips.map((tip, index) => (
490
+ <li key={index}>
491
+ <Typography variant="body2">{tip}</Typography>
492
+ </li>
493
+ ))}
494
+ </ul>
495
+ </>
496
+ )}
497
+ </div>
498
+ )}
499
+ </CardContent>
500
+ </Card>
501
+ );
502
+
503
+ default:
504
+ return null;
505
+ }
506
+ };
507
+
508
+ return (
509
+ <Paper className={classes.root}>
510
+ <Box p={3}>
511
+ <Typography variant="h5" gutterBottom>
512
+ Product Comparison Analysis
513
+ </Typography>
514
+ <Typography variant="body1" paragraph>
515
+ Upload product images to receive detailed information and comparison analysis.
516
+ You can analyze various products including cars, smartphones, laptops, and more.
517
+ </Typography>
518
+
519
+ <Grid container spacing={3}>
520
+ {/* 이미지 μ—…λ‘œλ“œ μ˜μ—­ */}
521
+ {[0, 1].map((index) => (
522
+ <Grid item xs={12} md={6} key={index}>
523
+ <Box className={classes.imageContainer}>
524
+ {imagePreviews[index] ? (
525
+ <>
526
+ <img
527
+ src={imagePreviews[index]}
528
+ alt={`Product ${index + 1}`}
529
+ className={classes.imagePreview}
530
+ />
531
+ <IconButton
532
+ className={classes.deleteButton}
533
+ onClick={() => handleImageDelete(index)}
534
+ >
535
+ <Delete />
536
+ </IconButton>
537
+ </>
538
+ ) : (
539
+ <>
540
+ <input
541
+ accept="image/*"
542
+ className={classes.uploadInput}
543
+ id={`upload-image-${index}`}
544
+ type="file"
545
+ onChange={(e) => handleImageUpload(e, index)}
546
+ />
547
+ <label htmlFor={`upload-image-${index}`}>
548
+ <Box display="flex" flexDirection="column" alignItems="center">
549
+ <AddCircle className={classes.uploadIcon} />
550
+ <Typography variant="body2" style={{ marginTop: '8px' }}>
551
+ Upload {index === 0 ? 'Product A' : 'Product B'} Image
552
+ </Typography>
553
+ </Box>
554
+ </label>
555
+ </>
556
+ )}
557
+ </Box>
558
+ </Grid>
559
+ ))}
560
+
561
+ {/* 뢄석 μœ ν˜• νƒ­ */}
562
+ <Grid item xs={12}>
563
+ <Paper>
564
+ <Tabs
565
+ value={tabValue}
566
+ onChange={handleTabChange}
567
+ indicatorColor="primary"
568
+ textColor="primary"
569
+ centered
570
+ >
571
+ <Tab icon={<Info />} label="Product Info" />
572
+ <Tab icon={<Compare />} label="Performance" disabled={!images[0] || !images[1]} />
573
+ <Tab icon={<Search />} label="Value Analysis" />
574
+ <Tab label="Recommendations" />
575
+ </Tabs>
576
+
577
+ <Box p={2} textAlign="center">
578
+ <Button
579
+ variant="contained"
580
+ color="primary"
581
+ onClick={handleAnalysis}
582
+ disabled={isProcessing || (!images[0] && !images[1])}
583
+ startIcon={isProcessing ? <CircularProgress size={24} /> : null}
584
+ >
585
+ {isProcessing ? 'Analyzing...' : 'Start Analysis'}
586
+ </Button>
587
+ </Box>
588
+
589
+ {/* 였λ₯˜ λ©”μ‹œμ§€ ν‘œμ‹œ */}
590
+ {error && (
591
+ <Box p={2} bgcolor="#ffebee" borderRadius="4px" mb={2}>
592
+ <Typography color="error">{error}</Typography>
593
+ </Box>
594
+ )}
595
+ </Paper>
596
+ </Grid>
597
+
598
+ {/* μ§„ν–‰ κ³Όμ • 둜그 ν‘œμ‹œ */}
599
+ <Grid item xs={12}>
600
+ <Paper>
601
+ <Box p={2}>
602
+ <Typography variant="h6" gutterBottom>
603
+ Analysis Progress
604
+ </Typography>
605
+ <Box className={classes.progressLog}>
606
+ {progressLogs.length === 0 ? (
607
+ <Typography variant="body2" color="textSecondary" style={{padding: '10px'}}>
608
+ Progress details will appear here when analysis starts.
609
+ </Typography>
610
+ ) : (
611
+ progressLogs.map((log, index) => (
612
+ <Box
613
+ key={index}
614
+ className={`${classes.logEntry} ${log.type === 'agent' ? classes.logEntryAgent : log.type === 'system' ? classes.logEntrySystem : log.type === 'error' ? classes.logEntryError : ''}`}
615
+ >
616
+ <span className={classes.logTime}>[{log.time}]</span>
617
+ {log.message}
618
+ </Box>
619
+ ))
620
+ )}
621
+ <div ref={logEndRef} />
622
+ </Box>
623
+ </Box>
624
+ </Paper>
625
+ </Grid>
626
+
627
+ {/* κ²°κ³Ό ν‘œμ‹œ μ˜μ—­ */}
628
+ <Grid item xs={12}>
629
+ {isProcessing ? (
630
+ <Box className={classes.loadingContainer}>
631
+ <CircularProgress />
632
+ <Typography variant="h6" style={{ marginTop: '16px' }}>
633
+ Analyzing Products...
634
+ </Typography>
635
+ <Typography variant="body2" color="textSecondary">
636
+ Product recognition, information retrieval, and comparison analysis in progress.
637
+ </Typography>
638
+ </Box>
639
+ ) : renderAnalysisResults()}
640
+ </Grid>
641
+ </Grid>
642
+ </Box>
643
+ </Paper>
644
+ );
645
+ };
646
+
647
+ export default ProductComparison;
product_comparison.py ADDED
@@ -0,0 +1,1108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Product Comparison Multi-Agent System
3
+
4
+ This module implements a multi-agent system for product comparison based on images.
5
+ The system uses various specialized agents to process images, extract features,
6
+ compare products, and provide recommendations.
7
+
8
+ Main components:
9
+ - Coordinator: Orchestrates the multi-agent workflow
10
+ - ImageProcessingAgent: Handles image recognition and object detection
11
+ - FeatureExtractionAgent: Extracts product specifications and features
12
+ - ComparisonAgent: Compares products based on extracted features
13
+ - RecommendationAgent: Provides purchase recommendations
14
+
15
+ Each agent utilizes vision models and LLMs to perform its specialized tasks.
16
+ """
17
+
18
+ import os
19
+ import uuid
20
+ import json
21
+ import time
22
+ from typing import Dict, List, Any, Optional, Tuple
23
+ import base64
24
+ from io import BytesIO
25
+ from datetime import datetime
26
+ from threading import Thread
27
+
28
+ import torch
29
+ from PIL import Image
30
+ import numpy as np
31
+
32
+ # Import LangChain components for agent implementation
33
+ try:
34
+ from langchain_core.prompts import ChatPromptTemplate
35
+ from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
36
+ from langchain.agents import create_openai_functions_agent
37
+ from langchain.agents import AgentExecutor
38
+ from langchain.memory import ConversationBufferMemory
39
+ from langchain.tools.render import format_tool_to_openai_function
40
+ from langchain_community.chat_models import ChatOpenAI
41
+ from langchain_community.tools.python.tool import PythonAstREPLTool
42
+ except ImportError:
43
+ print("Warning: LangChain components not available. Installing required packages...")
44
+ import subprocess
45
+ subprocess.run(["pip", "install", "langchain", "langchain-openai", "langchain-community"])
46
+ from langchain_core.prompts import ChatPromptTemplate
47
+ from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
48
+ from langchain.agents import create_openai_functions_agent
49
+ from langchain.agents import AgentExecutor
50
+ from langchain.memory import ConversationBufferMemory
51
+ from langchain.tools.render import format_tool_to_openai_function
52
+ from langchain_community.chat_models import ChatOpenAI
53
+ from langchain_community.tools.python.tool import PythonAstREPLTool
54
+
55
+ # Import vision models if available
56
+ try:
57
+ from ultralytics import YOLO
58
+ except ImportError:
59
+ YOLO = None
60
+
61
+ try:
62
+ from transformers import CLIPProcessor, CLIPModel
63
+ from transformers import AutoModelForCausalLM, AutoTokenizer
64
+ from transformers import ViTImageProcessor, ViTForImageClassification
65
+ except ImportError:
66
+ CLIPProcessor = None
67
+ CLIPModel = None
68
+ AutoModelForCausalLM = None
69
+ AutoTokenizer = None
70
+ ViTImageProcessor = None
71
+ ViTForImageClassification = None
72
+
73
+ # Session storage for SSE communication
74
+ class SessionManager:
75
+ """Manages active product comparison sessions and their event streams"""
76
+
77
+ def __init__(self):
78
+ self.active_sessions = {}
79
+ self.results = {}
80
+
81
+ def create_session(self, session_id=None):
82
+ """Create a new session with unique ID"""
83
+ if session_id is None:
84
+ session_id = str(uuid.uuid4())
85
+
86
+ self.active_sessions[session_id] = {
87
+ "created_at": datetime.now(),
88
+ "messages": [],
89
+ "status": "created"
90
+ }
91
+ return session_id
92
+
93
+ def add_message(self, session_id, message, agent_type="system"):
94
+ """Add a message to the session event stream"""
95
+ if session_id not in self.active_sessions:
96
+ return False
97
+
98
+ timestamp = datetime.now().strftime("%H:%M:%S")
99
+ self.active_sessions[session_id]["messages"].append({
100
+ "message": message,
101
+ "agent": agent_type,
102
+ "timestamp": timestamp
103
+ })
104
+ return True
105
+
106
+ def get_messages(self, session_id):
107
+ """Get all messages for a session"""
108
+ if session_id not in self.active_sessions:
109
+ return []
110
+ return self.active_sessions[session_id]["messages"]
111
+
112
+ def set_final_result(self, session_id, result):
113
+ """Store the final analysis result for a session"""
114
+ self.results[session_id] = result
115
+ self.active_sessions[session_id]["status"] = "completed"
116
+
117
+ def get_final_result(self, session_id):
118
+ """Get the final result for a session"""
119
+ return self.results.get(session_id)
120
+
121
+ def set_status(self, session_id, status):
122
+ """Update session status"""
123
+ if session_id in self.active_sessions:
124
+ self.active_sessions[session_id]["status"] = status
125
+
126
+ def get_status(self, session_id):
127
+ """Get session status"""
128
+ if session_id in self.active_sessions:
129
+ return self.active_sessions[session_id]["status"]
130
+ return None
131
+
132
+ def clean_old_sessions(self, max_age_hours=24):
133
+ """Clean up old sessions"""
134
+ now = datetime.now()
135
+ sessions_to_remove = []
136
+
137
+ for session_id, session_data in self.active_sessions.items():
138
+ age = now - session_data["created_at"]
139
+ if age.total_seconds() > max_age_hours * 3600:
140
+ sessions_to_remove.append(session_id)
141
+
142
+ for session_id in sessions_to_remove:
143
+ del self.active_sessions[session_id]
144
+ if session_id in self.results:
145
+ del self.results[session_id]
146
+
147
+ # Initialize the session manager
148
+ session_manager = SessionManager()
149
+
150
+ # Base Agent Class
151
+ class BaseAgent:
152
+ """Base class for all specialized agents"""
153
+
154
+ def __init__(self, name, llm=None):
155
+ self.name = name
156
+
157
+ # Use TinyLlama as the default LLM if none is provided
158
+ if llm is None:
159
+ try:
160
+ # Initialize TinyLlama model
161
+ model_name = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
162
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
163
+ model = AutoModelForCausalLM.from_pretrained(
164
+ model_name,
165
+ torch_dtype=torch.float16,
166
+ ).to("cuda" if torch.cuda.is_available() else "cpu")
167
+
168
+ # Wrap the model for LangChain
169
+ self.llm = self._create_llm_wrapper(model, tokenizer)
170
+ except Exception as e:
171
+ print(f"Error loading TinyLlama: {e}")
172
+ # Fallback to a simple string output
173
+ self.llm = None
174
+ else:
175
+ self.llm = llm
176
+
177
+ def _create_llm_wrapper(self, model, tokenizer):
178
+ """Create a simple wrapper for the LLM model"""
179
+ # This is a simplified wrapper - in production, you'd use a proper LangChain integration
180
+ def generate_text(prompt, max_tokens=512):
181
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
182
+ with torch.no_grad():
183
+ output = model.generate(
184
+ **inputs,
185
+ max_new_tokens=max_tokens,
186
+ temperature=0.7,
187
+ do_sample=True
188
+ )
189
+ response = tokenizer.decode(output[0], skip_special_tokens=True)
190
+ # Remove the prompt from the response
191
+ if response.startswith(prompt):
192
+ response = response[len(prompt):].strip()
193
+ return response
194
+
195
+ return generate_text
196
+
197
+ def log(self, session_id, message):
198
+ """Log a message to the session"""
199
+ return session_manager.add_message(session_id, message, agent_type=self.name)
200
+
201
+ async def process(self, session_id, data):
202
+ """Process data with this agent - to be implemented by subclasses"""
203
+ raise NotImplementedError("Subclasses must implement this method")
204
+
205
+
206
+ class ImageProcessingAgent(BaseAgent):
207
+ """Agent responsible for image processing and object recognition
208
+
209
+ This agent uses computer vision models to detect product type, brand, model,
210
+ and other visual characteristics from product images.
211
+ """
212
+
213
+ def __init__(self, name="ImageProcessingAgent"):
214
+ super().__init__(name)
215
+ # Initialize vision models
216
+ self.models = self._load_vision_models()
217
+
218
+ def _load_vision_models(self):
219
+ """Load vision models for product recognition"""
220
+ models = {}
221
+
222
+ # Try to load YOLO for object detection
223
+ try:
224
+ if YOLO is not None:
225
+ models["yolo"] = YOLO("yolov8n.pt")
226
+ except Exception as e:
227
+ print(f"Error loading YOLO: {e}")
228
+
229
+ # Try to load ViT for image classification
230
+ try:
231
+ if ViTImageProcessor is not None and ViTForImageClassification is not None:
232
+ models["vit_processor"] = ViTImageProcessor.from_pretrained("google/vit-base-patch16-224")
233
+ models["vit_model"] = ViTForImageClassification.from_pretrained("google/vit-base-patch16-224")
234
+ except Exception as e:
235
+ print(f"Error loading ViT: {e}")
236
+
237
+ # Try to load CLIP for visual embedding
238
+ try:
239
+ if CLIPProcessor is not None and CLIPModel is not None:
240
+ models["clip_processor"] = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")
241
+ models["clip_model"] = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
242
+ except Exception as e:
243
+ print(f"Error loading CLIP: {e}")
244
+
245
+ return models
246
+
247
+ def _process_with_yolo(self, image):
248
+ """Process image with YOLO for object detection"""
249
+ if "yolo" not in self.models:
250
+ return {"error": "YOLO model not available"}
251
+
252
+ # Convert image to numpy array if it's a PIL image
253
+ if isinstance(image, Image.Image):
254
+ image_np = np.array(image)
255
+ else:
256
+ image_np = image
257
+
258
+ # Run inference
259
+ results = self.models["yolo"](image_np)
260
+
261
+ # Extract detection results
262
+ detections = []
263
+ for result in results:
264
+ boxes = result.boxes
265
+ for box in boxes:
266
+ class_id = int(box.cls[0].item())
267
+ class_name = result.names[class_id]
268
+ confidence = round(box.conf[0].item(), 2)
269
+ bbox = box.xyxy[0].tolist()
270
+ bbox = [round(x) for x in bbox]
271
+
272
+ detections.append({
273
+ "class": class_name,
274
+ "confidence": confidence,
275
+ "bbox": bbox
276
+ })
277
+
278
+ return {"detections": detections}
279
+
280
+ def _process_with_vit(self, image):
281
+ """Process image with ViT for classification"""
282
+ if "vit_model" not in self.models or "vit_processor" not in self.models:
283
+ return {"error": "ViT model not available"}
284
+
285
+ # Prepare image for the model
286
+ inputs = self.models["vit_processor"](images=image, return_tensors="pt")
287
+
288
+ # Run inference
289
+ with torch.no_grad():
290
+ outputs = self.models["vit_model"](**inputs)
291
+ logits = outputs.logits
292
+
293
+ # Get top 5 predictions
294
+ probs = torch.nn.functional.softmax(logits, dim=-1)[0]
295
+ top5_prob, top5_indices = torch.topk(probs, 5)
296
+
297
+ results = []
298
+ for i, (prob, idx) in enumerate(zip(top5_prob, top5_indices)):
299
+ class_name = self.models["vit_model"].config.id2label[idx.item()]
300
+ results.append({
301
+ "rank": i+1,
302
+ "class": class_name,
303
+ "probability": round(prob.item(), 3)
304
+ })
305
+
306
+ return {"classifications": results}
307
+
308
+ def _extract_product_info_from_vision(self, image, results):
309
+ """Extract product information using LLM and vision results"""
310
+ if self.llm is None:
311
+ # Provide a basic extraction based on detection results only
312
+ if "detections" in results and results["detections"]:
313
+ detections = results["detections"]
314
+ # Get the most confident detection
315
+ top_detection = max(detections, key=lambda x: x["confidence"])
316
+ return {
317
+ "product_type": top_detection["class"],
318
+ "confidence": top_detection["confidence"]
319
+ }
320
+ return {"product_type": "unknown"}
321
+
322
+ # If we have an LLM, we can do more sophisticated extraction
323
+ prompt = f"""Analyze this product image detection results and extract detailed product information.
324
+ Detection results: {json.dumps(results)}
325
+
326
+ Extract the following information in JSON format:
327
+ - product_type: The category of the product (car, smartphone, laptop, etc.)
328
+ - brand: The most likely brand of the product
329
+ - model: Any model information that can be determined
330
+ - color: The main color of the product
331
+ - key_features: List of notable visual features
332
+ """
333
+
334
+ try:
335
+ response = self.llm(prompt)
336
+ # Try to parse as JSON
337
+ try:
338
+ extracted = json.loads(response)
339
+ return extracted
340
+ except json.JSONDecodeError:
341
+ # If LLM output is not valid JSON, extract key information using simple parsing
342
+ lines = response.split('\n')
343
+ extracted = {}
344
+ for line in lines:
345
+ if ':' in line:
346
+ key, value = line.split(':', 1)
347
+ key = key.strip().lower().replace(' ', '_')
348
+ value = value.strip().strip('"').strip("'")
349
+ if key and value:
350
+ extracted[key] = value
351
+ return extracted
352
+ except Exception as e:
353
+ print(f"Error extracting product info: {e}")
354
+ return {"product_type": "unknown", "error": str(e)}
355
+
356
+ async def process(self, session_id, data):
357
+ """Process product images to identify products and extract features"""
358
+ self.log(session_id, "Starting image analysis to identify products...")
359
+
360
+ results = {}
361
+ product_info = {}
362
+
363
+ # Process each image if available
364
+ images = data.get("images", [])
365
+ for i, img in enumerate(images):
366
+ if img is None:
367
+ continue
368
+
369
+ image_key = f"image{i+1}"
370
+ self.log(session_id, f"Processing {image_key}...")
371
+
372
+ # Run object detection
373
+ yolo_results = self._process_with_yolo(img)
374
+
375
+ # Run classification
376
+ vit_results = self._process_with_vit(img)
377
+
378
+ # Combine results
379
+ vision_results = {
380
+ **yolo_results,
381
+ **vit_results
382
+ }
383
+
384
+ # Extract product information from vision results
385
+ info = self._extract_product_info_from_vision(img, vision_results)
386
+
387
+ self.log(session_id, f"Identified product in {image_key}: {info.get('product_type', 'unknown')}")
388
+ if "brand" in info:
389
+ self.log(session_id, f"Detected brand: {info['brand']}")
390
+
391
+ results[image_key] = {
392
+ "vision_results": vision_results,
393
+ "product_info": info
394
+ }
395
+ product_info[image_key] = info
396
+
397
+ self.log(session_id, "Image processing completed")
398
+ return {
399
+ "vision_results": results,
400
+ "product_info": product_info
401
+ }
402
+
403
+
404
+ class FeatureExtractionAgent(BaseAgent):
405
+ """Agent responsible for extracting detailed product features and specifications
406
+
407
+ This agent analyzes image processing results and uses LLMs to extract detailed
408
+ product specifications, features, and technical details.
409
+ """
410
+
411
+ def __init__(self, name="FeatureExtractionAgent"):
412
+ super().__init__(name)
413
+
414
+ def _extract_specifications(self, product_info):
415
+ """Extract detailed specifications from product information"""
416
+ if self.llm is None:
417
+ # If no LLM is available, return basic specs from product info
418
+ return product_info
419
+
420
+ # Prepare prompt for specification extraction
421
+ product_type = product_info.get("product_type", "unknown")
422
+ prompt = f"""Based on this product information, generate a detailed list of specifications.
423
+ Product information: {json.dumps(product_info)}
424
+
425
+ For a {product_type}, extract or infer the following specifications in JSON format:
426
+ """
427
+
428
+ # Add product-specific specification types based on product type
429
+ if "car" in product_type.lower() or "vehicle" in product_type.lower():
430
+ prompt += """
431
+ - make: The manufacturer of the car
432
+ - model: The model name
433
+ - year: Estimated year of manufacture
434
+ - body_type: The body style (sedan, SUV, etc.)
435
+ - fuel_type: The fuel type if identifiable
436
+ - engine: Engine specifications if identifiable
437
+ - color: Exterior color
438
+ - features: List of visible features
439
+ """
440
+ elif "phone" in product_type.lower() or "smartphone" in product_type.lower():
441
+ prompt += """
442
+ - brand: The manufacturer
443
+ - model: The phone model
444
+ - screen_size: Estimated screen size
445
+ - camera: Visible camera specifications
446
+ - color: Device color
447
+ - generation: Device generation if identifiable
448
+ - features: List of visible features
449
+ """
450
+ elif "laptop" in product_type.lower() or "computer" in product_type.lower():
451
+ prompt += """
452
+ - brand: The manufacturer
453
+ - model: The computer model
454
+ - screen_size: Estimated screen size
455
+ - form_factor: Laptop, desktop, all-in-one, etc.
456
+ - color: Device color
457
+ - features: List of visible features
458
+ """
459
+ else:
460
+ # Generic product specifications
461
+ prompt += """
462
+ - brand: The manufacturer if identifiable
463
+ - model: The product model if identifiable
464
+ - color: Product color
465
+ - dimensions: Estimated dimensions
466
+ - features: List of visible features
467
+ - materials: Visible materials used
468
+ """
469
+
470
+ try:
471
+ response = self.llm(prompt)
472
+ # Try to parse as JSON
473
+ try:
474
+ specs = json.loads(response)
475
+ return specs
476
+ except json.JSONDecodeError:
477
+ # If LLM output is not valid JSON, extract key information using simple parsing
478
+ lines = response.split('\n')
479
+ specs = {}
480
+ for line in lines:
481
+ if ':' in line:
482
+ key, value = line.split(':', 1)
483
+ key = key.strip().lower().replace(' ', '_').strip('-').strip('_')
484
+ value = value.strip().strip('"').strip("'")
485
+ if key and value:
486
+ specs[key] = value
487
+ return specs
488
+ except Exception as e:
489
+ print(f"Error extracting specifications: {e}")
490
+ return {"error": str(e)}
491
+
492
+ def _get_feature_highlights(self, specs):
493
+ """Extract key feature highlights from specifications"""
494
+ if not specs or not isinstance(specs, dict):
495
+ return []
496
+
497
+ highlights = []
498
+
499
+ # Extract key features based on product type
500
+ product_type = specs.get("product_type", "").lower()
501
+
502
+ if "car" in product_type or "vehicle" in product_type:
503
+ # Highlight car features
504
+ if "make" in specs and "model" in specs:
505
+ highlights.append(f"{specs['make']} {specs['model']}")
506
+ if "year" in specs:
507
+ highlights.append(f"{specs['year']} model")
508
+ if "engine" in specs:
509
+ highlights.append(f"Engine: {specs['engine']}")
510
+ if "body_type" in specs:
511
+ highlights.append(f"{specs['body_type']} body style")
512
+
513
+ elif "phone" in product_type or "smartphone" in product_type:
514
+ # Highlight phone features
515
+ if "brand" in specs and "model" in specs:
516
+ highlights.append(f"{specs['brand']} {specs['model']}")
517
+ if "screen_size" in specs:
518
+ highlights.append(f"{specs['screen_size']} display")
519
+ if "camera" in specs:
520
+ highlights.append(f"Camera: {specs['camera']}")
521
+
522
+ elif "laptop" in product_type or "computer" in product_type:
523
+ # Highlight laptop features
524
+ if "brand" in specs and "model" in specs:
525
+ highlights.append(f"{specs['brand']} {specs['model']}")
526
+ if "screen_size" in specs:
527
+ highlights.append(f"{specs['screen_size']} display")
528
+
529
+ # Generic highlights for any product
530
+ if "features" in specs:
531
+ if isinstance(specs["features"], list):
532
+ highlights.extend(specs["features"][:3]) # Top 3 features
533
+ elif isinstance(specs["features"], str):
534
+ features = specs["features"].split(",")
535
+ highlights.extend([f.strip() for f in features[:3]]) # Top 3 features
536
+
537
+ # Add color as a feature if available
538
+ if "color" in specs:
539
+ highlights.append(f"{specs['color']} color")
540
+
541
+ return highlights
542
+
543
+ async def process(self, session_id, data):
544
+ """Process product information to extract detailed specifications"""
545
+ self.log(session_id, "Extracting detailed product specifications...")
546
+
547
+ results = {}
548
+ product_info = data.get("product_info", {})
549
+
550
+ if not product_info:
551
+ self.log(session_id, "No product information available for specification extraction")
552
+ return {"specifications": {}}
553
+
554
+ # Process each product
555
+ for key, info in product_info.items():
556
+ self.log(session_id, f"Extracting specifications for {key}...")
557
+
558
+ # Extract detailed specifications
559
+ specs = self._extract_specifications(info)
560
+
561
+ # Get feature highlights
562
+ highlights = self._get_feature_highlights(specs)
563
+
564
+ # Log results
565
+ if highlights:
566
+ self.log(session_id, f"Key features for {key}: {', '.join(highlights[:3])}")
567
+
568
+ results[key] = {
569
+ "specifications": specs,
570
+ "highlights": highlights
571
+ }
572
+
573
+ self.log(session_id, "Feature extraction completed")
574
+ return {"specifications": results}
575
+
576
+
577
+ class ComparisonAgent(BaseAgent):
578
+ """Agent responsible for comparing products based on their specifications
579
+
580
+ This agent analyzes the specifications of multiple products and identifies
581
+ the key differences, advantages, and disadvantages between them.
582
+ """
583
+
584
+ def __init__(self, name="ComparisonAgent"):
585
+ super().__init__(name)
586
+
587
+ def _compare_specifications(self, specs1, specs2):
588
+ """Compare two sets of product specifications"""
589
+ if not specs1 or not specs2:
590
+ return {"error": "Insufficient specification data for comparison"}
591
+
592
+ # If we have an LLM, use it for more sophisticated comparison
593
+ if self.llm is not None:
594
+ prompt = f"""Compare these two products based on their specifications.
595
+ Product 1: {json.dumps(specs1)}
596
+ Product 2: {json.dumps(specs2)}
597
+
598
+ Provide a detailed comparison in JSON format with the following structure:
599
+ {{"differences": [...], "product1_advantages": [...], "product2_advantages": [...], "summary": "..."}}
600
+
601
+ - differences: List key differences between the products
602
+ - product1_advantages: List advantages of Product 1 over Product 2
603
+ - product2_advantages: List advantages of Product 2 over Product 1
604
+ - summary: A concise summary of the comparison
605
+ """
606
+
607
+ try:
608
+ response = self.llm(prompt)
609
+ # Try to parse as JSON
610
+ try:
611
+ comparison = json.loads(response)
612
+ return comparison
613
+ except json.JSONDecodeError:
614
+ # If LLM output is not valid JSON, extract key sections using simple parsing
615
+ sections = {}
616
+ current_section = None
617
+ section_content = []
618
+
619
+ lines = response.split('\n')
620
+ for line in lines:
621
+ line = line.strip()
622
+ if not line:
623
+ continue
624
+
625
+ if line.endswith(":") and not line.startswith("-"):
626
+ # This is a section header
627
+ if current_section and section_content:
628
+ sections[current_section] = section_content
629
+ section_content = []
630
+
631
+ current_section = line.strip(":").lower().replace(" ", "_")
632
+ elif line.startswith("-") and current_section:
633
+ # This is a list item
634
+ section_content.append(line.strip("- "))
635
+ elif current_section:
636
+ # This is content for the current section
637
+ section_content.append(line)
638
+
639
+ # Add the last section
640
+ if current_section and section_content:
641
+ sections[current_section] = section_content
642
+
643
+ return sections
644
+ except Exception as e:
645
+ print(f"Error in LLM comparison: {e}")
646
+ # Fall back to simple comparison
647
+ pass
648
+
649
+ # Simple comparison logic as fallback
650
+ differences = []
651
+ product1_advantages = []
652
+ product2_advantages = []
653
+
654
+ # Identify common keys to compare
655
+ all_keys = set(list(specs1.keys()) + list(specs2.keys()))
656
+
657
+ # Exclude utility keys like 'error'
658
+ exclude_keys = {'error', 'product_type', 'confidence'}
659
+ compare_keys = all_keys - exclude_keys
660
+
661
+ for key in compare_keys:
662
+ val1 = specs1.get(key)
663
+ val2 = specs2.get(key)
664
+
665
+ if val1 is None and val2 is not None:
666
+ differences.append(f"Product 2 has {key}: {val2}, but Product 1 doesn't")
667
+ product2_advantages.append(f"Has {key}: {val2}")
668
+ elif val1 is not None and val2 is None:
669
+ differences.append(f"Product 1 has {key}: {val1}, but Product 2 doesn't")
670
+ product1_advantages.append(f"Has {key}: {val1}")
671
+ elif val1 != val2:
672
+ differences.append(f"Different {key}: Product 1 has {val1}, Product 2 has {val2}")
673
+
674
+ # Try to determine advantages based on common metrics
675
+ if key in ['price', 'weight']:
676
+ # Lower is generally better
677
+ try:
678
+ num1 = float(str(val1).split()[0])
679
+ num2 = float(str(val2).split()[0])
680
+ if num1 < num2:
681
+ product1_advantages.append(f"Lower {key}: {val1}")
682
+ else:
683
+ product2_advantages.append(f"Lower {key}: {val2}")
684
+ except (ValueError, IndexError):
685
+ pass
686
+ elif key in ['screen_size', 'storage', 'memory', 'ram', 'battery', 'capacity']:
687
+ # Higher is generally better
688
+ try:
689
+ num1 = float(str(val1).split()[0])
690
+ num2 = float(str(val2).split()[0])
691
+ if num1 > num2:
692
+ product1_advantages.append(f"Higher {key}: {val1}")
693
+ else:
694
+ product2_advantages.append(f"Higher {key}: {val2}")
695
+ except (ValueError, IndexError):
696
+ pass
697
+
698
+ # Create a simple summary
699
+ product1_type = specs1.get('product_type', 'Product 1')
700
+ product2_type = specs2.get('product_type', 'Product 2')
701
+
702
+ summary = f"Comparison between {product1_type} and {product2_type} reveals {len(differences)} key differences."
703
+
704
+ if len(product1_advantages) > len(product2_advantages):
705
+ summary += f" {product1_type} appears to have more advantages."
706
+ elif len(product2_advantages) > len(product1_advantages):
707
+ summary += f" {product2_type} appears to have more advantages."
708
+ else:
709
+ summary += " Both products have similar number of advantages."
710
+
711
+ return {
712
+ "differences": differences,
713
+ "product1_advantages": product1_advantages,
714
+ "product2_advantages": product2_advantages,
715
+ "summary": summary
716
+ }
717
+
718
+ async def process(self, session_id, data):
719
+ """Compare products based on their specifications"""
720
+ self.log(session_id, "Starting product comparison analysis...")
721
+
722
+ specifications = data.get("specifications", {})
723
+ if len(specifications) < 2:
724
+ self.log(session_id, "Not enough products to compare")
725
+ return {"comparison": {"error": "Need at least two products to compare"}}
726
+
727
+ # Get the product keys (image1, image2, etc.)
728
+ product_keys = list(specifications.keys())
729
+
730
+ if len(product_keys) > 2:
731
+ self.log(session_id, f"Found {len(product_keys)} products, comparing the first two only")
732
+ product_keys = product_keys[:2]
733
+
734
+ # Get the specifications for each product
735
+ product1_specs = specifications.get(product_keys[0], {}).get("specifications", {})
736
+ product2_specs = specifications.get(product_keys[1], {}).get("specifications", {})
737
+
738
+ # Perform comparison
739
+ comparison = self._compare_specifications(product1_specs, product2_specs)
740
+
741
+ # Log comparison results
742
+ if "differences" in comparison:
743
+ num_diff = len(comparison["differences"])
744
+ self.log(session_id, f"Found {num_diff} key differences between the products")
745
+
746
+ # Log a few example differences
747
+ if num_diff > 0:
748
+ for i, diff in enumerate(comparison["differences"][:3]):
749
+ self.log(session_id, f"Difference {i+1}: {diff}")
750
+
751
+ # Log summary if available
752
+ if "summary" in comparison:
753
+ self.log(session_id, f"Comparison summary: {comparison['summary']}")
754
+
755
+ self.log(session_id, "Comparison analysis completed")
756
+ return {
757
+ "comparison": comparison,
758
+ "product_keys": product_keys
759
+ }
760
+
761
+
762
+ class RecommendationAgent(BaseAgent):
763
+ """Agent responsible for providing purchase recommendations
764
+
765
+ This agent analyzes product comparisons and provides personalized recommendations
766
+ based on the user's needs and preferences.
767
+ """
768
+
769
+ def __init__(self, name="RecommendationAgent"):
770
+ super().__init__(name)
771
+
772
+ def _generate_recommendation(self, comparison, product_keys, specifications):
773
+ """Generate a purchase recommendation based on comparison results"""
774
+ if not comparison or "error" in comparison:
775
+ return {
776
+ "recommendation": "Insufficient data to make a recommendation",
777
+ "reason": "Could not compare the products",
778
+ "confidence": 0.0
779
+ }
780
+
781
+ # Get product information
782
+ product1_key = product_keys[0]
783
+ product2_key = product_keys[1]
784
+ product1_specs = specifications.get(product1_key, {}).get("specifications", {})
785
+ product2_specs = specifications.get(product2_key, {}).get("specifications", {})
786
+
787
+ product1_type = product1_specs.get("product_type", product1_key)
788
+ product2_type = product2_specs.get("product_type", product2_key)
789
+
790
+ # If we have an LLM, use it for more sophisticated recommendation
791
+ if self.llm is not None:
792
+ prompt = f"""Based on this product comparison, provide a purchase recommendation.
793
+ Product 1: {json.dumps(product1_specs)}
794
+ Product 2: {json.dumps(product2_specs)}
795
+ Comparison: {json.dumps(comparison)}
796
+
797
+ Provide a recommendation in JSON format with the following structure:
798
+ {{"recommended_product": "1 or 2", "recommendation": "...", "reason": "...", "confidence": 0.0-1.0, "use_cases": [...]}}
799
+
800
+ - recommended_product: Either "1" or "2" indicating which product is recommended
801
+ - recommendation: A concise recommendation statement
802
+ - reason: The main reason for the recommendation
803
+ - confidence: A confidence score between 0 and 1
804
+ - use_cases: List of ideal use cases for the recommended product
805
+ """
806
+
807
+ try:
808
+ response = self.llm(prompt)
809
+ # Try to parse as JSON
810
+ try:
811
+ recommendation = json.loads(response)
812
+ return recommendation
813
+ except json.JSONDecodeError:
814
+ # If LLM output is not valid JSON, extract key information using simple parsing
815
+ lines = response.split('\n')
816
+ recommendation = {}
817
+
818
+ # Look for recommendation indicator
819
+ for line in lines:
820
+ if "recommended product" in line.lower() and ("1" in line or "2" in line):
821
+ recommendation["recommended_product"] = "1" if "1" in line else "2"
822
+ elif "recommendation" in line.lower() and ":" in line:
823
+ recommendation["recommendation"] = line.split(":", 1)[1].strip().strip('"')
824
+ elif "reason" in line.lower() and ":" in line:
825
+ recommendation["reason"] = line.split(":", 1)[1].strip().strip('"')
826
+ elif "confidence" in line.lower() and ":" in line:
827
+ try:
828
+ conf_str = line.split(":", 1)[1].strip()
829
+ conf = float(conf_str)
830
+ recommendation["confidence"] = conf
831
+ except ValueError:
832
+ recommendation["confidence"] = 0.5
833
+ elif "use cases" in line.lower() and ":" in line:
834
+ use_cases = line.split(":", 1)[1].strip().strip('"')
835
+ recommendation["use_cases"] = [uc.strip() for uc in use_cases.split(",")]
836
+
837
+ return recommendation
838
+ except Exception as e:
839
+ print(f"Error in LLM recommendation: {e}")
840
+ # Fall back to simple recommendation
841
+ pass
842
+
843
+ # Simple recommendation logic as fallback
844
+ product1_advantages = comparison.get("product1_advantages", [])
845
+ product2_advantages = comparison.get("product2_advantages", [])
846
+
847
+ # Count advantages
848
+ p1_count = len(product1_advantages)
849
+ p2_count = len(product2_advantages)
850
+
851
+ # Simple confidence calculation
852
+ total_advantages = p1_count + p2_count
853
+ if total_advantages == 0:
854
+ confidence = 0.5 # Can't determine
855
+ else:
856
+ # Maximum confidence is 0.95, minimum is 0.55
857
+ max_confidence = 0.95
858
+ min_confidence = 0.55
859
+ base_confidence = max(p1_count, p2_count) / total_advantages if total_advantages > 0 else 0.5
860
+ confidence = min_confidence + base_confidence * (max_confidence - min_confidence)
861
+
862
+ # Make recommendation
863
+ if p1_count > p2_count:
864
+ recommended = "1"
865
+ recommendation = f"{product1_type} is recommended over {product2_type}"
866
+ reason = f"It has {p1_count} advantages compared to {p2_count} for the alternative"
867
+ elif p2_count > p1_count:
868
+ recommended = "2"
869
+ recommendation = f"{product2_type} is recommended over {product1_type}"
870
+ reason = f"It has {p2_count} advantages compared to {p1_count} for the alternative"
871
+ else:
872
+ # Equal advantages, slight preference for first product
873
+ recommended = "1"
874
+ recommendation = f"Both {product1_type} and {product2_type} appear equally matched"
875
+ reason = "Consider your specific needs as both have similar advantages"
876
+ confidence = 0.5 # Equal confidence
877
+
878
+ # Determine use cases based on advantages
879
+ use_cases = []
880
+
881
+ if recommended == "1" and product1_advantages:
882
+ # Extract use cases from product 1's advantages
883
+ use_cases = [f"When {adv.lower()}" for adv in product1_advantages[:3]]
884
+ elif recommended == "2" and product2_advantages:
885
+ # Extract use cases from product 2's advantages
886
+ use_cases = [f"When {adv.lower()}" for adv in product2_advantages[:3]]
887
+
888
+ # Add generic use case if none found
889
+ if not use_cases:
890
+ use_cases = [f"General {product1_type if recommended == '1' else product2_type} usage"]
891
+
892
+ return {
893
+ "recommended_product": recommended,
894
+ "recommendation": recommendation,
895
+ "reason": reason,
896
+ "confidence": round(confidence, 2),
897
+ "use_cases": use_cases
898
+ }
899
+
900
+ async def process(self, session_id, data):
901
+ """Generate purchase recommendations based on product comparison"""
902
+ self.log(session_id, "Generating product recommendations...")
903
+
904
+ # Get comparison data
905
+ comparison = data.get("comparison", {})
906
+ product_keys = data.get("product_keys", [])
907
+ specifications = data.get("specifications", {})
908
+
909
+ if not comparison or not product_keys or len(product_keys) < 2:
910
+ self.log(session_id, "Insufficient data for recommendation")
911
+ return {"recommendation": {"error": "Not enough data to generate recommendation"}}
912
+
913
+ # Generate recommendation
914
+ recommendation = self._generate_recommendation(comparison, product_keys, specifications)
915
+
916
+ # Log recommendation
917
+ self.log(session_id, f"Recommendation: {recommendation.get('recommendation', 'No recommendation')}")
918
+ self.log(session_id, f"Reason: {recommendation.get('reason', 'No reason provided')}")
919
+
920
+ # Log confidence
921
+ confidence = recommendation.get('confidence', 0)
922
+ confidence_percent = f"{int(confidence * 100)}%"
923
+ self.log(session_id, f"Confidence in recommendation: {confidence_percent}")
924
+
925
+ self.log(session_id, "Recommendation generation completed")
926
+ return {"recommendation": recommendation}
927
+
928
+
929
+ class ProductComparisonCoordinator:
930
+ """Main coordinator for the product comparison multi-agent system
931
+
932
+ This class orchestrates the entire product comparison workflow by managing
933
+ all the specialized agents and their interactions.
934
+ """
935
+
936
+ def __init__(self):
937
+ # Initialize all agents
938
+ self.image_processor = ImageProcessingAgent()
939
+ self.feature_extractor = FeatureExtractionAgent()
940
+ self.comparison_agent = ComparisonAgent()
941
+ self.recommendation_agent = RecommendationAgent()
942
+
943
+ async def process_images(self, session_id, images, session_metadata=None):
944
+ """Process images through the entire multi-agent workflow
945
+
946
+ Args:
947
+ session_id: Unique session identifier
948
+ images: List of image data (PIL Images or numpy arrays)
949
+ session_metadata: Optional dictionary with additional session information
950
+
951
+ Returns:
952
+ Dictionary containing the final analysis results
953
+ """
954
+ # Initialize session if it doesn't exist
955
+ if session_manager.get_status(session_id) is None:
956
+ session_manager.create_session(session_id)
957
+
958
+ # Set default metadata if not provided
959
+ if session_metadata is None:
960
+ session_metadata = {}
961
+
962
+ # Get analysis type from metadata
963
+ analysis_type = session_metadata.get('analysis_type', 'info')
964
+
965
+ session_manager.set_status(session_id, "processing")
966
+ session_manager.add_message(session_id, f"Starting product {analysis_type} analysis")
967
+
968
+ try:
969
+ # Step 1: Process images with Image Processing Agent
970
+ session_manager.add_message(session_id, "Step 1: Analyzing product images...")
971
+ image_results = await self.image_processor.process(session_id, {"images": images})
972
+
973
+ # Check if we have enough products to compare
974
+ product_info = image_results.get("product_info", {})
975
+ if len(product_info) < 1:
976
+ session_manager.add_message(session_id, "Error: No products detected in images")
977
+ session_manager.set_status(session_id, "error")
978
+ return {"error": "No products detected in images"}
979
+
980
+ # Step 2: Extract features with Feature Extraction Agent
981
+ session_manager.add_message(session_id, "Step 2: Extracting product specifications...")
982
+ feature_results = await self.feature_extractor.process(session_id, image_results)
983
+
984
+ # Step 3: Compare products with Comparison Agent if we have multiple products
985
+ comparison_results = {}
986
+ if len(product_info) >= 2:
987
+ session_manager.add_message(session_id, "Step 3: Comparing products...")
988
+ comparison_results = await self.comparison_agent.process(
989
+ session_id,
990
+ {**feature_results, "specifications": feature_results.get("specifications", {})}
991
+ )
992
+
993
+ # Step 4: Generate recommendation with Recommendation Agent
994
+ session_manager.add_message(session_id, "Step 4: Generating purchase recommendation...")
995
+ recommendation_results = await self.recommendation_agent.process(
996
+ session_id,
997
+ {**comparison_results, "specifications": feature_results.get("specifications", {})}
998
+ )
999
+ else:
1000
+ session_manager.add_message(session_id, "Skipping comparison: Only one product detected")
1001
+ comparison_results = {"comparison": {"error": "Need at least two products to compare"}}
1002
+ recommendation_results = {"recommendation": {"error": "Need at least two products to compare"}}
1003
+
1004
+ # Tailor results based on analysis type
1005
+ final_results = {
1006
+ "status": "completed"
1007
+ }
1008
+
1009
+ # Include results based on analysis type
1010
+ if analysis_type == 'info':
1011
+ final_results["productInfo"] = image_results.get("product_info", {})
1012
+ final_results["specifications"] = feature_results.get("specifications", {})
1013
+
1014
+ elif analysis_type == 'compare':
1015
+ final_results["comparison"] = comparison_results.get("comparison", {})
1016
+ final_results["productInfo"] = image_results.get("product_info", {})
1017
+
1018
+ elif analysis_type == 'value':
1019
+ # Value analysis combines specs and comparison data
1020
+ final_results["valueAnalysis"] = {
1021
+ "priceA": "$" + str(1000 + int(hash(str(session_id)) % 500)), # Mock price for demo
1022
+ "valueScoreA": 7 + (int(hash(str(session_id)) % 3)), # Mock score between 7-9
1023
+ "analysis": "Based on the specifications and market positioning, this product offers good value for money."
1024
+ }
1025
+ if len(product_info) >= 2:
1026
+ final_results["valueAnalysis"]["priceB"] = "$" + str(1200 + int(hash(str(session_id + "B")) % 500))
1027
+ final_results["valueAnalysis"]["valueScoreB"] = 6 + (int(hash(str(session_id + "B")) % 4))
1028
+
1029
+ elif analysis_type == 'recommend':
1030
+ if recommendation_results.get("recommendation", {}).get("error"):
1031
+ # Create a mock recommendation if only one product
1032
+ if len(product_info) == 1:
1033
+ product_name = next(iter(product_info.values())).get("name", "Product")
1034
+ final_results["recommendation"] = {
1035
+ "recommendedProduct": product_name,
1036
+ "reason": "This is the only product analyzed and appears to meet standard quality benchmarks.",
1037
+ "confidence": 0.85,
1038
+ "alternatives": [
1039
+ {"name": "Similar model with higher storage", "reason": "If you need more storage capacity"},
1040
+ {"name": "Budget alternative", "reason": "If price is your primary concern"}
1041
+ ],
1042
+ "buyingTips": [
1043
+ "Wait for seasonal sales for the best price",
1044
+ "Check for warranty terms before purchasing"
1045
+ ]
1046
+ }
1047
+ else:
1048
+ final_results["recommendation"] = recommendation_results.get("recommendation", {})
1049
+ else:
1050
+ final_results["recommendation"] = recommendation_results.get("recommendation", {})
1051
+
1052
+ # Always include basic product info and vision results for context
1053
+ final_results["vision_results"] = image_results.get("vision_results", {})
1054
+ if "productInfo" not in final_results: # Don't duplicate if already added
1055
+ final_results["productInfo"] = image_results.get("product_info", {})
1056
+
1057
+ # Set final results in session manager
1058
+ session_manager.set_final_result(session_id, final_results)
1059
+ session_manager.set_status(session_id, "completed")
1060
+ session_manager.add_message(session_id, "Product comparison analysis completed successfully")
1061
+
1062
+ return final_results
1063
+
1064
+ except Exception as e:
1065
+ error_msg = f"Error during product comparison: {str(e)}"
1066
+ session_manager.add_message(session_id, error_msg)
1067
+ session_manager.set_status(session_id, "error")
1068
+ return {"error": error_msg}
1069
+
1070
+ def get_session_messages(self, session_id):
1071
+ """Get all messages for a session"""
1072
+ return session_manager.get_messages(session_id)
1073
+
1074
+ def get_session_result(self, session_id):
1075
+ """Get the final result for a session"""
1076
+ return session_manager.get_final_result(session_id)
1077
+
1078
+ def get_session_status(self, session_id):
1079
+ """Get the status of a session"""
1080
+ return session_manager.get_status(session_id)
1081
+
1082
+
1083
+ # Helper function to create a coordinator instance
1084
+ def get_product_comparison_coordinator():
1085
+ """Get a singleton instance of the ProductComparisonCoordinator"""
1086
+ if not hasattr(get_product_comparison_coordinator, "_instance"):
1087
+ get_product_comparison_coordinator._instance = ProductComparisonCoordinator()
1088
+ return get_product_comparison_coordinator._instance
1089
+
1090
+
1091
+ # Helper function to convert base64 image data to PIL Image
1092
+ def decode_base64_image(base64_data):
1093
+ """Convert base64 image data to PIL Image"""
1094
+ try:
1095
+ # Check if the base64 data includes a data URL prefix
1096
+ if base64_data.startswith('data:image'):
1097
+ # Extract the actual base64 data after the comma
1098
+ base64_data = base64_data.split(',', 1)[1]
1099
+
1100
+ # Decode base64 data
1101
+ image_data = base64.b64decode(base64_data)
1102
+
1103
+ # Convert to PIL Image
1104
+ image = Image.open(BytesIO(image_data))
1105
+ return image
1106
+ except Exception as e:
1107
+ print(f"Error decoding base64 image: {e}")
1108
+ return None
static/asset-manifest.json CHANGED
@@ -1,24 +1,24 @@
1
  {
2
  "files": {
3
- "main.css": "/static/css/main.3b4eede1.chunk.css",
4
- "main.js": "/static/js/main.fb4939ae.chunk.js",
5
- "main.js.map": "/static/js/main.fb4939ae.chunk.js.map",
6
  "runtime-main.js": "/static/js/runtime-main.25710301.js",
7
  "runtime-main.js.map": "/static/js/runtime-main.25710301.js.map",
8
- "static/js/2.9b5b80e8.chunk.js": "/static/js/2.9b5b80e8.chunk.js",
9
- "static/js/2.9b5b80e8.chunk.js.map": "/static/js/2.9b5b80e8.chunk.js.map",
10
  "static/js/3.9013e23f.chunk.js": "/static/js/3.9013e23f.chunk.js",
11
  "static/js/3.9013e23f.chunk.js.map": "/static/js/3.9013e23f.chunk.js.map",
12
  "index.html": "/index.html",
13
- "precache-manifest.2fad43b74e0c6c48ddb79498a374f3f4.js": "/precache-manifest.2fad43b74e0c6c48ddb79498a374f3f4.js",
14
  "service-worker.js": "/service-worker.js",
15
- "static/css/main.3b4eede1.chunk.css.map": "/static/css/main.3b4eede1.chunk.css.map",
16
- "static/js/2.9b5b80e8.chunk.js.LICENSE.txt": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
17
  },
18
  "entrypoints": [
19
  "static/js/runtime-main.25710301.js",
20
- "static/js/2.9b5b80e8.chunk.js",
21
- "static/css/main.3b4eede1.chunk.css",
22
- "static/js/main.fb4939ae.chunk.js"
23
  ]
24
  }
 
1
  {
2
  "files": {
3
+ "main.css": "/static/css/main.59c2a54e.chunk.css",
4
+ "main.js": "/static/js/main.db88b5b1.chunk.js",
5
+ "main.js.map": "/static/js/main.db88b5b1.chunk.js.map",
6
  "runtime-main.js": "/static/js/runtime-main.25710301.js",
7
  "runtime-main.js.map": "/static/js/runtime-main.25710301.js.map",
8
+ "static/js/2.abf91741.chunk.js": "/static/js/2.abf91741.chunk.js",
9
+ "static/js/2.abf91741.chunk.js.map": "/static/js/2.abf91741.chunk.js.map",
10
  "static/js/3.9013e23f.chunk.js": "/static/js/3.9013e23f.chunk.js",
11
  "static/js/3.9013e23f.chunk.js.map": "/static/js/3.9013e23f.chunk.js.map",
12
  "index.html": "/index.html",
13
+ "precache-manifest.653a7aee73b10eaaa2fae0610652f6fa.js": "/precache-manifest.653a7aee73b10eaaa2fae0610652f6fa.js",
14
  "service-worker.js": "/service-worker.js",
15
+ "static/css/main.59c2a54e.chunk.css.map": "/static/css/main.59c2a54e.chunk.css.map",
16
+ "static/js/2.abf91741.chunk.js.LICENSE.txt": "/static/js/2.abf91741.chunk.js.LICENSE.txt"
17
  },
18
  "entrypoints": [
19
  "static/js/runtime-main.25710301.js",
20
+ "static/js/2.abf91741.chunk.js",
21
+ "static/css/main.59c2a54e.chunk.css",
22
+ "static/js/main.db88b5b1.chunk.js"
23
  ]
24
  }
static/index.html CHANGED
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Multi-Model Object Detection Demo"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Vision Web App</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link href="/static/css/main.3b4eede1.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"9013e23f"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="/static/js/2.9b5b80e8.chunk.js"></script><script src="/static/js/main.fb4939ae.chunk.js"></script></body></html>
 
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Multi-Model Object Detection Demo"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Vision Web App</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link href="/static/css/main.59c2a54e.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"9013e23f"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="/static/js/2.abf91741.chunk.js"></script><script src="/static/js/main.db88b5b1.chunk.js"></script></body></html>
static/precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js DELETED
@@ -1,30 +0,0 @@
1
- self.__precacheManifest = (self.__precacheManifest || []).concat([
2
- {
3
- "revision": "d54f7d4646b42d5859a3c60dc17c2cbc",
4
- "url": "/index.html"
5
- },
6
- {
7
- "revision": "bb5a62e98165b2cca977",
8
- "url": "/static/css/main.3b4eede1.chunk.css"
9
- },
10
- {
11
- "revision": "ca6d8d363680165da358",
12
- "url": "/static/js/2.9b5b80e8.chunk.js"
13
- },
14
- {
15
- "revision": "89a1b2dcd30c03705b2bceeb141b76b6",
16
- "url": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
17
- },
18
- {
19
- "revision": "70d68b45d511e1e11f23",
20
- "url": "/static/js/3.9013e23f.chunk.js"
21
- },
22
- {
23
- "revision": "bb5a62e98165b2cca977",
24
- "url": "/static/js/main.fc0e2dc7.chunk.js"
25
- },
26
- {
27
- "revision": "d8c310b0ac7ffa6d8151",
28
- "url": "/static/js/runtime-main.25710301.js"
29
- }
30
- ]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/{precache-manifest.2fad43b74e0c6c48ddb79498a374f3f4.js β†’ precache-manifest.653a7aee73b10eaaa2fae0610652f6fa.js} RENAMED
@@ -1,27 +1,27 @@
1
  self.__precacheManifest = (self.__precacheManifest || []).concat([
2
  {
3
- "revision": "65fe3b2853370de7fac7c1ee9bb1521e",
4
  "url": "/index.html"
5
  },
6
  {
7
- "revision": "62814d1f5d8f989e26a8",
8
- "url": "/static/css/main.3b4eede1.chunk.css"
9
  },
10
  {
11
- "revision": "ca6d8d363680165da358",
12
- "url": "/static/js/2.9b5b80e8.chunk.js"
13
  },
14
  {
15
  "revision": "89a1b2dcd30c03705b2bceeb141b76b6",
16
- "url": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
17
  },
18
  {
19
  "revision": "70d68b45d511e1e11f23",
20
  "url": "/static/js/3.9013e23f.chunk.js"
21
  },
22
  {
23
- "revision": "62814d1f5d8f989e26a8",
24
- "url": "/static/js/main.fb4939ae.chunk.js"
25
  },
26
  {
27
  "revision": "d8c310b0ac7ffa6d8151",
 
1
  self.__precacheManifest = (self.__precacheManifest || []).concat([
2
  {
3
+ "revision": "63e79b8c5a7091459c975e9ced1d4412",
4
  "url": "/index.html"
5
  },
6
  {
7
+ "revision": "d435db4fd542a4ef6aa8",
8
+ "url": "/static/css/main.59c2a54e.chunk.css"
9
  },
10
  {
11
+ "revision": "a3aa56e81892b1b7fdee",
12
+ "url": "/static/js/2.abf91741.chunk.js"
13
  },
14
  {
15
  "revision": "89a1b2dcd30c03705b2bceeb141b76b6",
16
+ "url": "/static/js/2.abf91741.chunk.js.LICENSE.txt"
17
  },
18
  {
19
  "revision": "70d68b45d511e1e11f23",
20
  "url": "/static/js/3.9013e23f.chunk.js"
21
  },
22
  {
23
+ "revision": "d435db4fd542a4ef6aa8",
24
+ "url": "/static/js/main.db88b5b1.chunk.js"
25
  },
26
  {
27
  "revision": "d8c310b0ac7ffa6d8151",
static/precache-manifest.90eb425b86312bf2b8dbe43e7d0377d8.js DELETED
@@ -1,30 +0,0 @@
1
- self.__precacheManifest = (self.__precacheManifest || []).concat([
2
- {
3
- "revision": "686c81215854d346608ddcf0fcd01a24",
4
- "url": "/index.html"
5
- },
6
- {
7
- "revision": "7eefab545d6bfce9770a",
8
- "url": "/static/css/main.3b4eede1.chunk.css"
9
- },
10
- {
11
- "revision": "ca6d8d363680165da358",
12
- "url": "/static/js/2.9b5b80e8.chunk.js"
13
- },
14
- {
15
- "revision": "89a1b2dcd30c03705b2bceeb141b76b6",
16
- "url": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
17
- },
18
- {
19
- "revision": "70d68b45d511e1e11f23",
20
- "url": "/static/js/3.9013e23f.chunk.js"
21
- },
22
- {
23
- "revision": "7eefab545d6bfce9770a",
24
- "url": "/static/js/main.975ea04a.chunk.js"
25
- },
26
- {
27
- "revision": "d8c310b0ac7ffa6d8151",
28
- "url": "/static/js/runtime-main.25710301.js"
29
- }
30
- ]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/precache-manifest.9a82b4d1673393a138e3168d2b6c841d.js DELETED
@@ -1,30 +0,0 @@
1
- self.__precacheManifest = (self.__precacheManifest || []).concat([
2
- {
3
- "revision": "b511568a479eef23388cdb305d8d56a3",
4
- "url": "/index.html"
5
- },
6
- {
7
- "revision": "eaece3a38555d76d4a73",
8
- "url": "/static/css/main.3b4eede1.chunk.css"
9
- },
10
- {
11
- "revision": "ca6d8d363680165da358",
12
- "url": "/static/js/2.9b5b80e8.chunk.js"
13
- },
14
- {
15
- "revision": "89a1b2dcd30c03705b2bceeb141b76b6",
16
- "url": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
17
- },
18
- {
19
- "revision": "70d68b45d511e1e11f23",
20
- "url": "/static/js/3.9013e23f.chunk.js"
21
- },
22
- {
23
- "revision": "eaece3a38555d76d4a73",
24
- "url": "/static/js/main.fe247ab2.chunk.js"
25
- },
26
- {
27
- "revision": "d8c310b0ac7ffa6d8151",
28
- "url": "/static/js/runtime-main.25710301.js"
29
- }
30
- ]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/service-worker.js CHANGED
@@ -14,7 +14,7 @@
14
  importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15
 
16
  importScripts(
17
- "/precache-manifest.2fad43b74e0c6c48ddb79498a374f3f4.js"
18
  );
19
 
20
  self.addEventListener('message', (event) => {
@@ -35,5 +35,5 @@ workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35
 
36
  workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
37
 
38
- blacklist: [/^\/_/,/\/[^\/?]+\.[^\/]+$/],
39
  });
 
14
  importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15
 
16
  importScripts(
17
+ "/precache-manifest.653a7aee73b10eaaa2fae0610652f6fa.js"
18
  );
19
 
20
  self.addEventListener('message', (event) => {
 
35
 
36
  workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
37
 
38
+ blacklist: [/^\/_/,/\/[^/?]+\.[^/]+$/],
39
  });
static/static/css/main.3b4eede1.chunk.css.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["index.css","App.css"],"names":[],"mappings":"AAAA,KACE,QAAS,CACT,mJAEY,CACZ,kCAAmC,CACnC,iCAAkC,CAClC,wBACF,CAEA,KACE,yEAEF,CCbA,KACE,iBACF,CAEA,eACE,UAAW,CACX,WAAY,CACZ,eAAgB,CAChB,kBAAmB,CACnB,aAAc,CACd,YACF,CAEA,cACE,cAAe,CACf,qBAAsB,CACtB,iBAAkB,CAClB,WACF,CAEA,gBACE,eAAgB,CAChB,eACF,CAEA,YACE,cAAe,CACf,kBACF,CAEA,kBACE,0BAA2B,CAC3B,qCACF,CAEA,qBACE,wBAAyB,CACzB,wBACF,CAEA,qBACE,UAAY,CACZ,kBACF,CAEA,kBACE,eAAgB,CAChB,eAAiB,CACjB,UACF","file":"main.3b4eede1.chunk.css","sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n background-color: #f5f5f5;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n",".App {\n text-align: center;\n}\n\n.preview-image {\n width: 100%;\n height: auto;\n max-height: 100%;\n object-fit: contain;\n display: block;\n margin-top: 0;\n}\n\n.result-image {\n max-width: 100%;\n border: 1px solid #ddd;\n border-radius: 4px;\n padding: 4px;\n}\n\n.detection-list {\n margin-top: 16px;\n text-align: left;\n}\n\n.model-card {\n cursor: pointer;\n transition: all 0.3s;\n}\n\n.model-card:hover {\n transform: translateY(-5px);\n box-shadow: 0 10px 20px rgba(0,0,0,0.1);\n}\n\n.model-card.selected {\n border: 2px solid #3f51b5;\n background-color: #e8eaf6;\n}\n\n.model-card.disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.performance-info {\n margin-top: 16px;\n font-size: 0.9rem;\n color: #666;\n}\n"]}
 
 
static/static/css/{main.3b4eede1.chunk.css β†’ main.59c2a54e.chunk.css} RENAMED
@@ -1,2 +1,2 @@
1
- body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:#f5f5f5}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}.App{text-align:center}.preview-image{width:100%;height:auto;max-height:100%;object-fit:contain;display:block;margin-top:0}.result-image{max-width:100%;border:1px solid #ddd;border-radius:4px;padding:4px}.detection-list{margin-top:16px;text-align:left}.model-card{cursor:pointer;transition:all .3s}.model-card:hover{transform:translateY(-5px);box-shadow:0 10px 20px rgba(0,0,0,.1)}.model-card.selected{border:2px solid #3f51b5;background-color:#e8eaf6}.model-card.disabled{opacity:.6;cursor:not-allowed}.performance-info{margin-top:16px;font-size:.9rem;color:#666}
2
- /*# sourceMappingURL=main.3b4eede1.chunk.css.map */
 
1
+ body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:#f5f5f5}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}.App{text-align:center}.preview-image{max-width:100%;max-height:300px;margin-top:16px}.result-image{max-width:100%;border:1px solid #ddd;border-radius:4px;padding:4px}.detection-list{margin-top:16px;text-align:left}.model-card{cursor:pointer;transition:all .3s}.model-card:hover{transform:translateY(-5px);box-shadow:0 10px 20px rgba(0,0,0,.1)}.model-card.selected{border:2px solid #3f51b5;background-color:#e8eaf6}.model-card.disabled{opacity:.6;cursor:not-allowed}.performance-info{margin-top:16px;font-size:.9rem;color:#666}
2
+ /*# sourceMappingURL=main.59c2a54e.chunk.css.map */
static/static/css/main.59c2a54e.chunk.css.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"sources":["index.css","App.css"],"names":[],"mappings":"AAAA,KACE,QAAS,CACT,mJAEY,CACZ,kCAAmC,CACnC,iCAAkC,CAClC,wBACF,CAEA,KACE,yEAEF,CCbA,KACE,iBACF,CAEA,eACE,cAAe,CACf,gBAAiB,CACjB,eACF,CAEA,cACE,cAAe,CACf,qBAAsB,CACtB,iBAAkB,CAClB,WACF,CAEA,gBACE,eAAgB,CAChB,eACF,CAEA,YACE,cAAe,CACf,kBACF,CAEA,kBACE,0BAA2B,CAC3B,qCACF,CAEA,qBACE,wBAAyB,CACzB,wBACF,CAEA,qBACE,UAAY,CACZ,kBACF,CAEA,kBACE,eAAgB,CAChB,eAAiB,CACjB,UACF","file":"main.59c2a54e.chunk.css","sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n background-color: #f5f5f5;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n",".App {\n text-align: center;\n}\n\n.preview-image {\n max-width: 100%;\n max-height: 300px;\n margin-top: 16px;\n}\n\n.result-image {\n max-width: 100%;\n border: 1px solid #ddd;\n border-radius: 4px;\n padding: 4px;\n}\n\n.detection-list {\n margin-top: 16px;\n text-align: left;\n}\n\n.model-card {\n cursor: pointer;\n transition: all 0.3s;\n}\n\n.model-card:hover {\n transform: translateY(-5px);\n box-shadow: 0 10px 20px rgba(0,0,0,0.1);\n}\n\n.model-card.selected {\n border: 2px solid #3f51b5;\n background-color: #e8eaf6;\n}\n\n.model-card.disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.performance-info {\n margin-top: 16px;\n font-size: 0.9rem;\n color: #666;\n}\n"]}
static/static/js/{2.9b5b80e8.chunk.js β†’ 2.abf91741.chunk.js} RENAMED
The diff for this file is too large to render. See raw diff
 
static/static/js/{2.9b5b80e8.chunk.js.LICENSE.txt β†’ 2.abf91741.chunk.js.LICENSE.txt} RENAMED
File without changes
static/static/js/{2.9b5b80e8.chunk.js.map β†’ 2.abf91741.chunk.js.map} RENAMED
The diff for this file is too large to render. See raw diff
 
static/static/js/main.975ea04a.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),u=t(48),g=t(158),p=t(138),E=t(81),b=t(143),h=t(136),y=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),j=t(133);const C=Object(j.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"60vh",[e.breakpoints.down("sm")]:{height:"45vh"},display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var w=e=>{let{onImageUpload:a}=e;const t=C(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},g=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&g(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&g(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image",style:{width:"100%",height:"auto",maxHeight:"100%",objectFit:"contain",display:"block"}}),r.a.createElement(y.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(h.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},O=t(139),N=t(140),B=t(166),T=t(141),I=t(64),k=t.n(I),P=t(76),D=t.n(P),F=t(77),R=t.n(F);const A=Object(j.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var _=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=A(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(D.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(O.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(T.a,null,r.a.createElement(h.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(h.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(R.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},z=t(153),L=t(150),W=t(105),M=t(154),J=t(142),H=t(164),U=t(163),V=t(145),G=t(146),Y=t(147),Q=t(167),q=t(160),K=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(j.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[g,E]=Object(n.useState)(!1),[y,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,j]=Object(n.useState)([]),[C,w]=Object(n.useState)(!1),[T,I]=Object(n.useState)(null),{model:k,data:P}=a,D=()=>{E(!1)},F=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(h.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,metadata:{model:"vit",classifications:P.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,objects:P.detections,imageId:F()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(b.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(h.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),j([]),I(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(H.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(U.a,{open:g,onClose:D,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(Q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(q.a,{labelId:"search-type-label",id:"search-type",value:y,onChange:e=>{f(e.target.value),j([]),I(null)}},r.a.createElement(K.a,{value:"image"},"Search by Current Image"),r.a.createElement(K.a,{value:"class"},"Search by Class Name"))),"class"===y&&r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),T&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},T),r.a.createElement(s.a,{className:t.searchResults},C?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(b.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(O.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(h.a,{onClick:D,color:"default"},"Close"),r.a.createElement(h.a,{onClick:async()=>{w(!0),I(null);try{let e={};if("image"===y)e={searchType:"image",image:P.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),j(t.results)}catch(e){console.error("Error searching vector DB:",e),I("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:C||"class"===y&&!v.trim()},"Search"))))};const te=Object(j.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(z.a,{className:t.dividerMargin}),r.a.createElement(u.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(L.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(M.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(z.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(L.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(M.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(z.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(j.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,y]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(h.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),y(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?y(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),y("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(b.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(z.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};var oe=function(e){let{imageBase64:a}=e;const[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(""),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(!1),[g,b]=Object(n.useState)(""),[y,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,j]=Object(n.useState)(""),[C,w]=Object(n.useState)(5);return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Vision RAG (LangChain)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"\uc11c\ubc84\uc5d0 OPENAI_API_KEY\uac00 \uc124\uc815\ub418\uc5b4 \uc788\ub2e4\uba74 API Key\ub294 \uc0dd\ub7b5 \uac00\ub2a5\ud569\ub2c8\ub2e4. \uac80\uc0c9 \uc720\ud615\uc744 \uc120\ud0dd\ud558\uace0 \uc9c8\ubb38\uc744 \ubcf4\ub0b4\uba74, \ubca1\ud130 DB\uc5d0\uc11c \uac80\uc0c9\ub41c \ucee8\ud14d\uc2a4\ud2b8\ub85c \ub2f5\ubcc0\ud569\ub2c8\ub2e4."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(Y.a,{fullWidth:!0,variant:"outlined",size:"small"},r.a.createElement(Q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(q.a,{labelId:"search-type-label",value:y,onChange:e=>f(e.target.value),label:"Search Type"},r.a.createElement(K.a,{value:"image"},"image (current upload)"),r.a.createElement(K.a,{value:"object"},"object (objectId)"),r.a.createElement(K.a,{value:"class"},"class (class_name)")))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),"object"===y&&r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"objectId",value:v,onChange:e=>x(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),"class"===y&&r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"class_name",value:S,onChange:e=>j(e.target.value),fullWidth:!0,variant:"outlined",size:"small",placeholder:"e.g. person, car, dog"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"n_results",value:C,onChange:e=>w(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"number",inputProps:{min:1,max:50}})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:o,onChange:e=>c(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined",placeholder:"image"===y?"\uc5c5\ub85c\ub4dc\ud55c \uc774\ubbf8\uc9c0 \uae30\ubc18\uc73c\ub85c \ub2f5\ubcc0\ud574\uc918":"\uac80\uc0c9\ub41c \uac1d\uccb4 \ucee8\ud14d\uc2a4\ud2b8\ub97c \uc0ac\uc6a9\ud574 \ub2f5\ubcc0\ud574\uc918"})),g&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},g)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(h.a,{color:"primary",variant:"contained",onClick:async()=>{b(""),s("");const e=(o||"").trim();if(!e)return void b("\uc9c8\ubb38\uc744 \uc785\ub825\ud558\uc138\uc694.");const n={userQuery:e,searchType:y,n_results:Number(C)||5};if(t&&(n.api_key=t),"image"===y){if(!a)return void b("\uc774\ubbf8\uc9c0\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uba3c\uc800 \uc774\ubbf8\uc9c0\ub97c \uc5c5\ub85c\ub4dc\ud558\uc138\uc694.");n.image=a}else if("object"===y){if(!v.trim())return void b("objectId\ub97c \uc785\ub825\ud558\uc138\uc694.");n.objectId=v.trim()}else if("class"===y){if(!S.trim())return void b("class_name\uc744 \uc785\ub825\ud558\uc138\uc694.");n.class_name=S.trim()}d(!0);try{const e=await fetch("/api/vision-rag/query",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(n)});if(!e.ok){let a=await e.text();try{a=JSON.stringify(JSON.parse(a),null,2)}catch(r){}throw new Error(a)}const a=await e.json(),t="Model: ".concat(a.model||"-"," | Latency: ").concat(a.latency_sec||"-","s");s((a.answer||"(\ube48 \uc751\ub2f5)")+"\n\n---\n"+t)}catch(l){b("Error: "+l.message)}finally{d(!1)}},disabled:m},m?"Sending...":"Send Question"),r.a.createElement(h.a,{variant:"outlined",onClick:()=>{c(""),s(""),b("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(z.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},i))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(""),[h,y]=Object(n.useState)(!1),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)(null),[j,C]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{C(e.models)}).catch(e=>{console.error("Error checking API status:",e),S("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(w,{onImageUpload:e=>{if(a(e),v(null),S(null),e instanceof File){const a=new FileReader;a.onload=()=>{const e=a.result;l("string"===typeof e?e:"")},a.onerror=()=>l(""),a.readAsDataURL(e)}else l("")}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(_,{onModelSelect:e=>{c(e),v(null),S(null)},onProcess:async()=>{if(!e||!o)return void S("Please select both an image and a model");y(!0),S(null);const a=new FormData;a.append("image",e);let t="";switch(o){case"yolo":t="/api/detect/yolo";break;case"detr":t="/api/detect/detr";break;case"vit":t="/api/classify/vit";break;default:return S("Invalid model selection"),void y(!1)}try{const e=await fetch(t,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const n=await e.json();v({model:o,data:n})}catch(n){console.error("Error processing image:",n),S("Error processing image: ".concat(n.message))}finally{y(!1)}},isProcessing:h,modelsStatus:j,selectedModel:o,imageSelected:!!e})),x&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},x))),h&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(b.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),f&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:f})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:f.data,model:f.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,{imageBase64:t}))))))};var se=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
- //# sourceMappingURL=main.975ea04a.chunk.js.map
 
 
 
static/static/js/main.975ea04a.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","components/OpenAIChat.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","breakpoints","down","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","maxHeight","objectFit","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","detections","imageId","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","OpenAIChat","imageBase64","apiKey","setApiKey","prompt","setPrompt","setResponse","loading","setLoading","objectId","setObjectId","setClassName","nResults","setNResults","inputProps","min","max","multiline","rows","q","api_key","res","credentials","txt","text","parse","_unused","meta","latency_sec","answer","onClear","fontFamily","createMuiTheme","typography","App","selectedImage","setSelectedImage","setImageBase64","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","File","reader","FileReader","onload","dataUrl","onerror","readAsDataURL","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.975ea04a.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n // Use viewport-based height so any aspect ratio fits inside\n height: '60vh',\n [theme.breakpoints.down('sm')]: {\n height: '45vh',\n },\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2),\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n style={{\n width: '100%',\n height: 'auto',\n maxHeight: '100%',\n objectFit: 'contain',\n display: 'block'\n }}\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-to-collection', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.detections,\n imageId: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n searchType: 'image',\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n searchType: 'class',\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\n )}\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState } from 'react';\nimport {\n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider,\n MenuItem,\n Select,\n FormControl,\n InputLabel\n} from '@material-ui/core';\n\n// Props:\n// - imageBase64: data URL string of the currently uploaded image (preferred for searchType=image)\nfunction OpenAIChat({ imageBase64 }) {\n const [apiKey, setApiKey] = useState('');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n // Vision RAG parameters\n const [searchType, setSearchType] = useState('image');\n const [objectId, setObjectId] = useState('');\n const [className, setClassName] = useState('');\n const [nResults, setNResults] = useState(5);\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const q = (prompt || '').trim();\n if (!q) { setError('μ§ˆλ¬Έμ„ μž…λ ₯ν•˜μ„Έμš”.'); return; }\n\n // Build request body for /api/vision-rag/query\n const body = {\n userQuery: q,\n searchType: searchType,\n n_results: Number(nResults) || 5,\n };\n if (apiKey) body.api_key = apiKey;\n if (searchType === 'image') {\n if (!imageBase64) { setError('이미지가 ν•„μš”ν•©λ‹ˆλ‹€. λ¨Όμ € 이미지λ₯Ό μ—…λ‘œλ“œν•˜μ„Έμš”.'); return; }\n body.image = imageBase64;\n } else if (searchType === 'object') {\n if (!objectId.trim()) { setError('objectIdλ₯Ό μž…λ ₯ν•˜μ„Έμš”.'); return; }\n body.objectId = objectId.trim();\n } else if (searchType === 'class') {\n if (!className.trim()) { setError('class_name을 μž…λ ₯ν•˜μ„Έμš”.'); return; }\n body.class_name = className.trim();\n }\n\n setLoading(true);\n try {\n const res = await fetch('/api/vision-rag/query', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body)\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model || '-'} | Latency: ${data.latency_sec || '-'}s`;\n setResponse((data.answer || '(빈 응닡)') + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n Vision RAG (LangChain)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n μ„œλ²„μ— OPENAI_API_KEYκ°€ μ„€μ •λ˜μ–΄ μžˆλ‹€λ©΄ API KeyλŠ” μƒλž΅ κ°€λŠ₯ν•©λ‹ˆλ‹€. 검색 μœ ν˜•μ„ μ„ νƒν•˜κ³  μ§ˆλ¬Έμ„ 보내면, 벑터 DBμ—μ„œ κ²€μƒ‰λœ μ»¨ν…μŠ€νŠΈλ‘œ λ‹΅λ³€ν•©λ‹ˆλ‹€.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <FormControl fullWidth variant=\"outlined\" size=\"small\">\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n value={searchType}\n onChange={(e) => setSearchType(e.target.value)}\n label=\"Search Type\"\n >\n <MenuItem value=\"image\">image (current upload)</MenuItem>\n <MenuItem value=\"object\">object (objectId)</MenuItem>\n <MenuItem value=\"class\">class (class_name)</MenuItem>\n </Select>\n </FormControl>\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n\n {searchType === 'object' && (\n <Grid item xs={12} md={6}>\n <TextField\n label=\"objectId\"\n value={objectId}\n onChange={(e) => setObjectId(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n )}\n {searchType === 'class' && (\n <Grid item xs={12} md={6}>\n <TextField\n label=\"class_name\"\n value={className}\n onChange={(e) => setClassName(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n placeholder=\"e.g. person, car, dog\"\n />\n </Grid>\n )}\n <Grid item xs={12} md={6}>\n <TextField\n label=\"n_results\"\n value={nResults}\n onChange={(e) => setNResults(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"number\"\n inputProps={{ min: 1, max: 50 }}\n />\n </Grid>\n\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n placeholder={searchType === 'image' ? 'μ—…λ‘œλ“œν•œ 이미지 기반으둜 λ‹΅λ³€ν•΄μ€˜' : 'κ²€μƒ‰λœ 객체 μ»¨ν…μŠ€νŠΈλ₯Ό μ‚¬μš©ν•΄ λ‹΅λ³€ν•΄μ€˜'}\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport OpenAIChat from './components/OpenAIChat';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [imageBase64, setImageBase64] = useState('');\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n\n // Convert uploaded File -> base64 data URL for Vision RAG image search\n if (image instanceof File) {\n const reader = new FileReader();\n reader.onload = () => {\n const dataUrl = reader.result; // e.g., data:image/png;base64,....\n setImageBase64(typeof dataUrl === 'string' ? dataUrl : '');\n };\n reader.onerror = () => setImageBase64('');\n reader.readAsDataURL(image);\n } else {\n setImageBase64('');\n }\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n\n {/* Vision RAG (LangChain) section at the end */}\n <Grid item xs={12}>\n <OpenAIChat imageBase64={imageBase64} />\n </Grid>\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
 
 
static/static/js/main.db88b5b1.chunk.js ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),u=t(48),g=t(158),p=t(138),E=t(81),h=t(143),y=t(136),b=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),C=t(133);const j=Object(C.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var O=e=>{let{onImageUpload:a}=e;const t=j(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},g=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&g(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&g(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image"}),r.a.createElement(b.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(y.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},w=t(139),N=t(140),B=t(165),T=t(141),I=t(64),k=t.n(I),P=t(76),D=t.n(P),A=t(77),F=t.n(A);const R=Object(C.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var z=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=R(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(D.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(w.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(T.a,null,r.a.createElement(y.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(y.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(F.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},L=t(153),M=t(150),W=t(105),_=t(154),J=t(142),U=t(163),H=t(164),V=t(145),G=t(146),Y=t(147),q=t(166),K=t(160),Q=t(151),X=t(168),Z=t(152),$=t(162);const ee=Object(C.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[g,E]=Object(n.useState)(!1),[b,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,C]=Object(n.useState)([]),[j,O]=Object(n.useState)(!1),[T,I]=Object(n.useState)(null),{model:k,data:P}=a,D=()=>{E(!1)},A=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,metadata:{model:"vit",classifications:P.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,objects:P.detections,imageId:A()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(h.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(y.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),C([]),I(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(U.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(H.a,{open:g,onClose:D,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(K.a,{labelId:"search-type-label",id:"search-type",value:b,onChange:e=>{f(e.target.value),C([]),I(null)}},r.a.createElement(Q.a,{value:"image"},"Search by Current Image"),r.a.createElement(Q.a,{value:"class"},"Search by Class Name"))),"class"===b&&r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),T&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},T),r.a.createElement(s.a,{className:t.searchResults},j?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(w.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(y.a,{onClick:D,color:"default"},"Close"),r.a.createElement(y.a,{onClick:async()=>{O(!0),I(null);try{let e={};if("image"===b)e={searchType:"image",image:P.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),C(t.results)}catch(e){console.error("Error searching vector DB:",e),I("Error searching vector DB: ".concat(e.message))}finally{O(!1)}},color:"primary",variant:"contained",disabled:j||"class"===b&&!v.trim()},"Search"))))};const te=Object(C.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(L.a,{className:t.dividerMargin}),r.a.createElement(u.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(M.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(M.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(C.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,b]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),b(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?b(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),b("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(h.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(L.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};var oe=function(){const[e,a]=Object(n.useState)("gpt-4o-mini"),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)("You are a helpful assistant."),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(""),[g,h]=Object(n.useState)(!1),[b,f]=Object(n.useState)("");return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"OpenAI Chat (OpenAI API)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"If the server env var OPENAI_API_KEY is set, the API Key field is optional."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"Model",value:e,onChange:e=>a(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"System Prompt (optional)",value:o,onChange:e=>c(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:i,onChange:e=>s(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined"})),b&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},b)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(y.a,{color:"primary",variant:"contained",onClick:async()=>{f(""),d("");const a=(i||"").trim();if(a){h(!0);try{const r=await fetch("/api/openai/chat",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({prompt:a,model:(e||"").trim()||"gpt-4o-mini",api_key:t||void 0,system:o||void 0})});if(!r.ok){let e=await r.text();try{e=JSON.stringify(JSON.parse(e),null,2)}catch(n){}throw new Error(e)}const l=await r.json(),c="Model: ".concat(l.model," | Latency: ").concat(l.latency_sec,"s")+(l.usage?" | Usage: ".concat(JSON.stringify(l.usage)):"");d((l.response||"(Empty response)")+"\n\n---\n"+c)}catch(r){f("Error: "+r.message)}finally{h(!1)}}else f("Please enter a question.")},disabled:g},g?"Sending...":"Send Question"),r.a.createElement(y.a,{variant:"outlined",onClick:()=>{s(""),d(""),f("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(L.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},m))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[y,b]=Object(n.useState)(null),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{S(e.models)}).catch(e=>{console.error("Error checking API status:",e),v("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(O,{onImageUpload:e=>{a(e),b(null),v(null)}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(z,{onModelSelect:e=>{l(e),b(null),v(null)},onProcess:async()=>{if(!e||!t)return void v("Please select both an image and a model");c(!0),v(null);const a=new FormData;a.append("image",e);let n="";switch(t){case"yolo":n="/api/detect/yolo";break;case"detr":n="/api/detect/detr";break;case"vit":n="/api/classify/vit";break;default:return v("Invalid model selection"),void c(!1)}try{const e=await fetch(n,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const r=await e.json();b({model:t,data:r})}catch(r){console.error("Error processing image:",r),v("Error processing image: ".concat(r.message))}finally{c(!1)}},isProcessing:o,modelsStatus:x,selectedModel:t,imageSelected:!!e})),f&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},f))),o&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),y&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:y})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:y.data,model:y.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,null))))))};var se=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
+ //# sourceMappingURL=main.db88b5b1.chunk.js.map
static/static/js/main.db88b5b1.chunk.js.map ADDED
@@ -0,0 +1 @@
 
 
1
+ {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","components/OpenAIChat.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","objectFit","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","detections","imageId","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","maxHeight","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","OpenAIChat","setModel","apiKey","setApiKey","system","setSystem","prompt","setPrompt","setResponse","loading","setLoading","multiline","rows","res","credentials","api_key","txt","text","parse","_unused","meta","latency_sec","usage","onClear","fontFamily","createMuiTheme","typography","App","selectedImage","setSelectedImage","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.db88b5b1.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n height: '100%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2)\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-to-collection', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.detections,\n imageId: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n searchType: 'image',\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n searchType: 'class',\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\n )}\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState } from 'react';\nimport { \n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider\n} from '@material-ui/core';\n\nfunction OpenAIChat() {\n const [model, setModel] = useState('gpt-4o-mini');\n const [apiKey, setApiKey] = useState('');\n const [system, setSystem] = useState('You are a helpful assistant.');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const p = (prompt || '').trim();\n if (!p) { setError('Please enter a question.'); return; }\n\n setLoading(true);\n try {\n const res = await fetch('/api/openai/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n prompt: p,\n model: (model || '').trim() || 'gpt-4o-mini',\n api_key: (apiKey || undefined),\n system: (system || undefined)\n })\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model} | Latency: ${data.latency_sec}s` + (data.usage ? ` | Usage: ${JSON.stringify(data.usage)}` : '');\n setResponse((data.response || '(Empty response)') + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n OpenAI Chat (OpenAI API)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n If the server env var OPENAI_API_KEY is set, the API Key field is optional.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"Model\"\n value={model}\n onChange={(e) => setModel(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"System Prompt (optional)\"\n value={system}\n onChange={(e) => setSystem(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport OpenAIChat from './components/OpenAIChat';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n\n {/* OpenAI Chat section at the end */}\n <Grid item xs={12}>\n <OpenAIChat />\n </Grid>\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
static/static/js/main.fb4939ae.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),u=t(48),g=t(158),p=t(138),E=t(81),h=t(143),b=t(136),y=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),j=t(133);const C=Object(j.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"60vh",[e.breakpoints.down("sm")]:{height:"45vh"},display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var w=e=>{let{onImageUpload:a}=e;const t=C(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},g=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&g(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&g(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image",style:{width:"100%",height:"auto",maxHeight:"100%",objectFit:"contain",display:"block"}}),r.a.createElement(y.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(b.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},O=t(139),N=t(140),B=t(166),I=t(141),T=t(64),k=t.n(T),P=t(76),D=t.n(P),F=t(77),A=t.n(F);const R=Object(j.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var _=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=R(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(D.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(O.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(I.a,null,r.a.createElement(b.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(b.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(A.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},z=t(153),L=t(150),W=t(105),M=t(154),J=t(142),H=t(164),U=t(163),V=t(145),G=t(146),q=t(147),Y=t(167),Q=t(160),K=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(j.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[g,E]=Object(n.useState)(!1),[y,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,j]=Object(n.useState)([]),[C,w]=Object(n.useState)(!1),[I,T]=Object(n.useState)(null),{model:k,data:P}=a,D=()=>{E(!1)},F=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(b.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,metadata:{model:"vit",classifications:P.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,objects:P.detections,imageId:F()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(h.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(b.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),j([]),T(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(H.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(U.a,{open:g,onClose:D,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(q.a,{className:t.formControl},r.a.createElement(Y.a,{id:"search-type-label"},"Search Type"),r.a.createElement(Q.a,{labelId:"search-type-label",id:"search-type",value:y,onChange:e=>{f(e.target.value),j([]),T(null)}},r.a.createElement(K.a,{value:"image"},"Search by Current Image"),r.a.createElement(K.a,{value:"class"},"Search by Class Name"))),"class"===y&&r.a.createElement(q.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),I&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},I),r.a.createElement(s.a,{className:t.searchResults},C?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(O.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(b.a,{onClick:D,color:"default"},"Close"),r.a.createElement(b.a,{onClick:async()=>{w(!0),T(null);try{let e={};if("image"===y)e={searchType:"image",image:P.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),j(t.results)}catch(e){console.error("Error searching vector DB:",e),T("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:C||"class"===y&&!v.trim()},"Search"))))};const te=Object(j.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(z.a,{className:t.dividerMargin}),r.a.createElement(u.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(L.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(M.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(z.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(L.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(M.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(z.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(j.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,y]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(b.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),y(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?y(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),y("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(h.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(z.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};var oe=function(e){let{imageBase64:a}=e;const[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(""),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(!1),[g,h]=Object(n.useState)(""),[y,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,j]=Object(n.useState)(""),[C,w]=Object(n.useState)(5);return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Vision RAG (LangChain)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"If OPENAI_API_KEY is set on the server, API Key is optional. Select a search type and send a question to get answers based on context retrieved from the vector DB."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(q.a,{fullWidth:!0,variant:"outlined",size:"small"},r.a.createElement(Y.a,{id:"search-type-label"},"Search Type"),r.a.createElement(Q.a,{labelId:"search-type-label",value:y,onChange:e=>f(e.target.value),label:"Search Type"},r.a.createElement(K.a,{value:"image"},"image (current upload)"),r.a.createElement(K.a,{value:"object"},"object (objectId)"),r.a.createElement(K.a,{value:"class"},"class (class_name)")))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),"object"===y&&r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"objectId",value:v,onChange:e=>x(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),"class"===y&&r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"class_name",value:S,onChange:e=>j(e.target.value),fullWidth:!0,variant:"outlined",size:"small",placeholder:"e.g. person, car, dog"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"n_results",value:C,onChange:e=>w(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"number",inputProps:{min:1,max:50}})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:o,onChange:e=>c(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined",placeholder:"image"===y?"Answer based on the uploaded image":"Answer using the retrieved object context"})),g&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},g)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(b.a,{color:"primary",variant:"contained",onClick:async()=>{h(""),s("");const e=(o||"").trim();if(!e)return void h("Please enter a question.");const n={userQuery:e,searchType:y,n_results:Number(C)||5};if(t&&(n.api_key=t),"image"===y){if(!a)return void h("Image is required. Please upload an image first.");n.image=a}else if("object"===y){if(!v.trim())return void h("Please enter an objectId.");n.objectId=v.trim()}else if("class"===y){if(!S.trim())return void h("Please enter a class_name.");n.class_name=S.trim()}d(!0);try{const e=await fetch("/api/vision-rag/query",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(n)});if(!e.ok){let a=await e.text();try{a=JSON.stringify(JSON.parse(a),null,2)}catch(r){}throw new Error(a)}const a=await e.json(),t="Model: ".concat(a.model||"-"," | Latency: ").concat(a.latency_sec||"-","s");let l="";a.retrieved&&a.retrieved.length>0&&(l="\n\nSearch Results:\n",a.retrieved.forEach((e,a)=>{l+="".concat(a+1,". ID: ").concat(e.id||"-","\n"),e.meta&&(l+=" Class: ".concat(e.meta.class||"-","\n"),l+=" Confidence: ".concat(e.meta.confidence||"-","\n")),l+=" Similarity: ".concat(e.distance?e.distance.toFixed(4):"-","\n")})),s((a.answer||"(No response)")+l+"\n\n---\n"+t)}catch(l){h("Error: "+l.message)}finally{d(!1)}},disabled:m},m?"Sending...":"Send Question"),r.a.createElement(b.a,{variant:"outlined",onClick:()=>{c(""),s(""),h("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(z.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},i))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(""),[b,y]=Object(n.useState)(!1),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)(null),[j,C]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{C(e.models)}).catch(e=>{console.error("Error checking API status:",e),S("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(w,{onImageUpload:e=>{if(a(e),v(null),S(null),e instanceof File){const a=new FileReader;a.onload=()=>{const e=a.result;l("string"===typeof e?e:"")},a.onerror=()=>l(""),a.readAsDataURL(e)}else l("")}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(_,{onModelSelect:e=>{c(e),v(null),S(null)},onProcess:async()=>{if(!e||!o)return void S("Please select both an image and a model");y(!0),S(null);const a=new FormData;a.append("image",e);let t="";switch(o){case"yolo":t="/api/detect/yolo";break;case"detr":t="/api/detect/detr";break;case"vit":t="/api/classify/vit";break;default:return S("Invalid model selection"),void y(!1)}try{const e=await fetch(t,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const n=await e.json();v({model:o,data:n})}catch(n){console.error("Error processing image:",n),S("Error processing image: ".concat(n.message))}finally{y(!1)}},isProcessing:b,modelsStatus:j,selectedModel:o,imageSelected:!!e})),x&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},x))),b&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),f&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:f})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:f.data,model:f.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,{imageBase64:t}))))))};var se=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
- //# sourceMappingURL=main.fb4939ae.chunk.js.map
 
 
 
static/static/js/main.fb4939ae.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","components/OpenAIChat.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","breakpoints","down","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","maxHeight","objectFit","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","detections","imageId","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","OpenAIChat","imageBase64","apiKey","setApiKey","prompt","setPrompt","setResponse","loading","setLoading","objectId","setObjectId","setClassName","nResults","setNResults","inputProps","min","max","multiline","rows","q","api_key","res","credentials","txt","text","parse","_unused","meta","latency_sec","retrievedText","retrieved","forEach","answer","onClear","fontFamily","createMuiTheme","typography","App","selectedImage","setSelectedImage","setImageBase64","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","File","reader","FileReader","onload","dataUrl","onerror","readAsDataURL","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.fb4939ae.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n // Use viewport-based height so any aspect ratio fits inside\n height: '60vh',\n [theme.breakpoints.down('sm')]: {\n height: '45vh',\n },\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2),\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n style={{\n width: '100%',\n height: 'auto',\n maxHeight: '100%',\n objectFit: 'contain',\n display: 'block'\n }}\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-to-collection', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.detections,\n imageId: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n searchType: 'image',\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n searchType: 'class',\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\n )}\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState } from 'react';\nimport {\n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider,\n MenuItem,\n Select,\n FormControl,\n InputLabel\n} from '@material-ui/core';\n\n// Props:\n// - imageBase64: data URL string of the currently uploaded image (preferred for searchType=image)\nfunction OpenAIChat({ imageBase64 }) {\n const [apiKey, setApiKey] = useState('');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n // Vision RAG parameters\n const [searchType, setSearchType] = useState('image');\n const [objectId, setObjectId] = useState('');\n const [className, setClassName] = useState('');\n const [nResults, setNResults] = useState(5);\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const q = (prompt || '').trim();\n if (!q) { setError('Please enter a question.'); return; }\n\n // Build request body for /api/vision-rag/query\n const body = {\n userQuery: q,\n searchType: searchType,\n n_results: Number(nResults) || 5,\n };\n if (apiKey) body.api_key = apiKey;\n if (searchType === 'image') {\n if (!imageBase64) { setError('Image is required. Please upload an image first.'); return; }\n body.image = imageBase64;\n } else if (searchType === 'object') {\n if (!objectId.trim()) { setError('Please enter an objectId.'); return; }\n body.objectId = objectId.trim();\n } else if (searchType === 'class') {\n if (!className.trim()) { setError('Please enter a class_name.'); return; }\n body.class_name = className.trim();\n }\n\n setLoading(true);\n try {\n const res = await fetch('/api/vision-rag/query', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body)\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model || '-'} | Latency: ${data.latency_sec || '-'}s`;\n \n // Format search results\n let retrievedText = '';\n if (data.retrieved && data.retrieved.length > 0) {\n retrievedText = '\\n\\nSearch Results:\\n';\n data.retrieved.forEach((item, index) => {\n retrievedText += `${index + 1}. ID: ${item.id || '-'}\\n`;\n if (item.meta) {\n retrievedText += ` Class: ${item.meta.class || '-'}\\n`;\n retrievedText += ` Confidence: ${item.meta.confidence || '-'}\\n`;\n }\n retrievedText += ` Similarity: ${item.distance ? item.distance.toFixed(4) : '-'}\\n`;\n });\n }\n \n setResponse((data.answer || '(No response)') + retrievedText + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n Vision RAG (LangChain)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n If OPENAI_API_KEY is set on the server, API Key is optional. Select a search type and send a question to get answers based on context retrieved from the vector DB.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <FormControl fullWidth variant=\"outlined\" size=\"small\">\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n value={searchType}\n onChange={(e) => setSearchType(e.target.value)}\n label=\"Search Type\"\n >\n <MenuItem value=\"image\">image (current upload)</MenuItem>\n <MenuItem value=\"object\">object (objectId)</MenuItem>\n <MenuItem value=\"class\">class (class_name)</MenuItem>\n </Select>\n </FormControl>\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n\n {searchType === 'object' && (\n <Grid item xs={12} md={6}>\n <TextField\n label=\"objectId\"\n value={objectId}\n onChange={(e) => setObjectId(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n )}\n {searchType === 'class' && (\n <Grid item xs={12} md={6}>\n <TextField\n label=\"class_name\"\n value={className}\n onChange={(e) => setClassName(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n placeholder=\"e.g. person, car, dog\"\n />\n </Grid>\n )}\n <Grid item xs={12} md={6}>\n <TextField\n label=\"n_results\"\n value={nResults}\n onChange={(e) => setNResults(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"number\"\n inputProps={{ min: 1, max: 50 }}\n />\n </Grid>\n\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n placeholder={searchType === 'image' ? 'Answer based on the uploaded image' : 'Answer using the retrieved object context'}\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport OpenAIChat from './components/OpenAIChat';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [imageBase64, setImageBase64] = useState('');\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n\n // Convert uploaded File -> base64 data URL for Vision RAG image search\n if (image instanceof File) {\n const reader = new FileReader();\n reader.onload = () => {\n const dataUrl = reader.result; // e.g., data:image/png;base64,....\n setImageBase64(typeof dataUrl === 'string' ? dataUrl : '');\n };\n reader.onerror = () => setImageBase64('');\n reader.readAsDataURL(image);\n } else {\n setImageBase64('');\n }\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n\n {/* Vision RAG (LangChain) section at the end */}\n <Grid item xs={12}>\n <OpenAIChat imageBase64={imageBase64} />\n </Grid>\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
 
 
static/static/js/main.fc0e2dc7.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),u=t(48),g=t(158),p=t(138),E=t(81),h=t(143),y=t(136),b=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),C=t(133);const j=Object(C.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"60vh",[e.breakpoints.down("sm")]:{height:"45vh"},display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var w=e=>{let{onImageUpload:a}=e;const t=j(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},g=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&g(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&g(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image",style:{width:"100%",height:"auto",maxHeight:"100%",objectFit:"contain",display:"block"}}),r.a.createElement(b.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(y.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},O=t(139),N=t(140),B=t(166),T=t(141),I=t(64),k=t.n(I),P=t(76),D=t.n(P),A=t(77),F=t.n(A);const R=Object(C.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var z=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=R(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(D.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(O.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(T.a,null,r.a.createElement(y.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(y.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(F.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},L=t(153),M=t(150),W=t(105),_=t(154),J=t(142),H=t(164),U=t(163),V=t(145),G=t(146),Y=t(147),q=t(167),K=t(160),Q=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(C.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[g,E]=Object(n.useState)(!1),[b,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,C]=Object(n.useState)([]),[j,w]=Object(n.useState)(!1),[T,I]=Object(n.useState)(null),{model:k,data:P}=a,D=()=>{E(!1)},A=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,metadata:{model:"vit",classifications:P.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,objects:P.detections,imageId:A()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(h.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(y.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),C([]),I(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(H.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(U.a,{open:g,onClose:D,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(K.a,{labelId:"search-type-label",id:"search-type",value:b,onChange:e=>{f(e.target.value),C([]),I(null)}},r.a.createElement(Q.a,{value:"image"},"Search by Current Image"),r.a.createElement(Q.a,{value:"class"},"Search by Class Name"))),"class"===b&&r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),T&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},T),r.a.createElement(s.a,{className:t.searchResults},j?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(O.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(y.a,{onClick:D,color:"default"},"Close"),r.a.createElement(y.a,{onClick:async()=>{w(!0),I(null);try{let e={};if("image"===b)e={searchType:"image",image:P.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),C(t.results)}catch(e){console.error("Error searching vector DB:",e),I("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:j||"class"===b&&!v.trim()},"Search"))))};const te=Object(C.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(L.a,{className:t.dividerMargin}),r.a.createElement(u.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(M.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(M.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(C.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,b]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),b(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?b(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),b("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(h.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(L.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};var oe=function(){const[e,a]=Object(n.useState)("gpt-4"),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)("You are a helpful assistant."),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(""),[g,h]=Object(n.useState)(!1),[b,f]=Object(n.useState)("");return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"OpenAI Chat (OpenAI API)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"If the server env var OPENAI_API_KEY is set, the API Key field is optional."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"Model",value:e,onChange:e=>a(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"System Prompt (optional)",value:o,onChange:e=>c(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:i,onChange:e=>s(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined"})),b&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},b)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(y.a,{color:"primary",variant:"contained",onClick:async()=>{f(""),d("");const a=(i||"").trim();if(a){h(!0);try{const r=await fetch("/api/openai/chat",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({prompt:a,model:(e||"").trim()||"gpt-4",api_key:t||void 0,system:o||void 0})});if(!r.ok){let e=await r.text();try{e=JSON.stringify(JSON.parse(e),null,2)}catch(n){}throw new Error(e)}const l=await r.json(),c="Model: ".concat(l.model," | Latency: ").concat(l.latency_sec,"s")+(l.usage?" | Usage: ".concat(JSON.stringify(l.usage)):"");d((l.response||"(Empty response)")+"\n\n---\n"+c)}catch(r){f("Error: "+r.message)}finally{h(!1)}}else f("Please enter a question.")},disabled:g},g?"Sending...":"Send Question"),r.a.createElement(y.a,{variant:"outlined",onClick:()=>{s(""),d(""),f("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(L.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},m))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[y,b]=Object(n.useState)(null),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{S(e.models)}).catch(e=>{console.error("Error checking API status:",e),v("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(w,{onImageUpload:e=>{a(e),b(null),v(null)}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(z,{onModelSelect:e=>{l(e),b(null),v(null)},onProcess:async()=>{if(!e||!t)return void v("Please select both an image and a model");c(!0),v(null);const a=new FormData;a.append("image",e);let n="";switch(t){case"yolo":n="/api/detect/yolo";break;case"detr":n="/api/detect/detr";break;case"vit":n="/api/classify/vit";break;default:return v("Invalid model selection"),void c(!1)}try{const e=await fetch(n,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const r=await e.json();b({model:t,data:r})}catch(r){console.error("Error processing image:",r),v("Error processing image: ".concat(r.message))}finally{c(!1)}},isProcessing:o,modelsStatus:x,selectedModel:t,imageSelected:!!e})),f&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},f))),o&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),y&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:y})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:y.data,model:y.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,null))))))};var se=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
- //# sourceMappingURL=main.fc0e2dc7.chunk.js.map
 
 
 
static/static/js/main.fc0e2dc7.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","components/OpenAIChat.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","breakpoints","down","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","maxHeight","objectFit","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","detections","imageId","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","OpenAIChat","setModel","apiKey","setApiKey","system","setSystem","prompt","setPrompt","setResponse","loading","setLoading","multiline","rows","res","credentials","api_key","txt","text","parse","_unused","meta","latency_sec","usage","onClear","fontFamily","createMuiTheme","typography","App","selectedImage","setSelectedImage","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.fc0e2dc7.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n // Use viewport-based height so any aspect ratio fits inside\n height: '60vh',\n [theme.breakpoints.down('sm')]: {\n height: '45vh',\n },\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2),\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n style={{\n width: '100%',\n height: 'auto',\n maxHeight: '100%',\n objectFit: 'contain',\n display: 'block'\n }}\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-to-collection', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.detections,\n imageId: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n searchType: 'image',\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n searchType: 'class',\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\n )}\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState } from 'react';\nimport { \n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider\n} from '@material-ui/core';\n\nfunction OpenAIChat() {\n const [model, setModel] = useState('gpt-4');\n const [apiKey, setApiKey] = useState('');\n const [system, setSystem] = useState('You are a helpful assistant.');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const p = (prompt || '').trim();\n if (!p) { setError('Please enter a question.'); return; }\n\n setLoading(true);\n try {\n const res = await fetch('/api/openai/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n prompt: p,\n model: (model || '').trim() || 'gpt-4',\n api_key: (apiKey || undefined),\n system: (system || undefined)\n })\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model} | Latency: ${data.latency_sec}s` + (data.usage ? ` | Usage: ${JSON.stringify(data.usage)}` : '');\n setResponse((data.response || '(Empty response)') + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n OpenAI Chat (OpenAI API)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n If the server env var OPENAI_API_KEY is set, the API Key field is optional.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"Model\"\n value={model}\n onChange={(e) => setModel(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"System Prompt (optional)\"\n value={system}\n onChange={(e) => setSystem(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport OpenAIChat from './components/OpenAIChat';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n\n {/* OpenAI Chat section at the end */}\n <Grid item xs={12}>\n <OpenAIChat />\n </Grid>\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
 
 
static/static/js/main.fe247ab2.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),u=t(48),g=t(158),p=t(138),E=t(81),b=t(143),h=t(136),y=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),j=t(133);const C=Object(j.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"60vh",[e.breakpoints.down("sm")]:{height:"45vh"},display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var w=e=>{let{onImageUpload:a}=e;const t=C(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},g=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&g(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&g(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image",style:{width:"100%",height:"auto",maxHeight:"100%",objectFit:"contain",display:"block"}}),r.a.createElement(y.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(h.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},O=t(139),N=t(140),B=t(166),T=t(141),I=t(64),k=t.n(I),D=t(76),P=t.n(D),F=t(77),R=t.n(F);const A=Object(j.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var _=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=A(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(P.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(O.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(T.a,null,r.a.createElement(h.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(h.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(R.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},z=t(153),L=t(150),W=t(105),M=t(154),J=t(142),H=t(164),U=t(163),V=t(145),G=t(146),Y=t(147),Q=t(167),q=t(160),K=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(j.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[g,E]=Object(n.useState)(!1),[y,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,j]=Object(n.useState)([]),[C,w]=Object(n.useState)(!1),[T,I]=Object(n.useState)(null),{model:k,data:D}=a,P=()=>{E(!1)},F=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(h.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:D.image,metadata:{model:"vit",classifications:D.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:D.image,objects:D.detections,imageId:F()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(b.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(h.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),j([]),I(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(H.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(U.a,{open:g,onClose:P,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(Q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(q.a,{labelId:"search-type-label",id:"search-type",value:y,onChange:e=>{f(e.target.value),j([]),I(null)}},r.a.createElement(K.a,{value:"image"},"Search by Current Image"),r.a.createElement(K.a,{value:"class"},"Search by Class Name"))),"class"===y&&r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),T&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},T),r.a.createElement(s.a,{className:t.searchResults},C?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(b.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(O.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(h.a,{onClick:P,color:"default"},"Close"),r.a.createElement(h.a,{onClick:async()=>{w(!0),I(null);try{let e={};if("image"===y)e={searchType:"image",image:D.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),j(t.results)}catch(e){console.error("Error searching vector DB:",e),I("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:C||"class"===y&&!v.trim()},"Search"))))};const te=Object(j.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(z.a,{className:t.dividerMargin}),r.a.createElement(u.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(L.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(M.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(z.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(L.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(M.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(z.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(j.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,y]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(h.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),y(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?y(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),y("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(b.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(z.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};var oe=function(e){let{imageBase64:a}=e;const[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(""),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(!1),[g,b]=Object(n.useState)(""),[y,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,j]=Object(n.useState)(""),[C,w]=Object(n.useState)(5);return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Vision RAG (LangChain)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"\uc11c\ubc84\uc5d0 OPENAI_API_KEY\uac00 \uc124\uc815\ub418\uc5b4 \uc788\ub2e4\uba74 API Key\ub294 \uc0dd\ub7b5 \uac00\ub2a5\ud569\ub2c8\ub2e4. \uac80\uc0c9 \uc720\ud615\uc744 \uc120\ud0dd\ud558\uace0 \uc9c8\ubb38\uc744 \ubcf4\ub0b4\uba74, \ubca1\ud130 DB\uc5d0\uc11c \uac80\uc0c9\ub41c \ucee8\ud14d\uc2a4\ud2b8\ub85c \ub2f5\ubcc0\ud569\ub2c8\ub2e4."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(Y.a,{fullWidth:!0,variant:"outlined",size:"small"},r.a.createElement(Q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(q.a,{labelId:"search-type-label",value:y,onChange:e=>f(e.target.value),label:"Search Type"},r.a.createElement(K.a,{value:"image"},"image (current upload)"),r.a.createElement(K.a,{value:"object"},"object (objectId)"),r.a.createElement(K.a,{value:"class"},"class (class_name)")))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),"object"===y&&r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"objectId",value:v,onChange:e=>x(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),"class"===y&&r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"class_name",value:S,onChange:e=>j(e.target.value),fullWidth:!0,variant:"outlined",size:"small",placeholder:"e.g. person, car, dog"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"n_results",value:C,onChange:e=>w(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"number",inputProps:{min:1,max:50}})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:o,onChange:e=>c(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined",placeholder:"image"===y?"\uc5c5\ub85c\ub4dc\ud55c \uc774\ubbf8\uc9c0 \uae30\ubc18\uc73c\ub85c \ub2f5\ubcc0\ud574\uc918":"\uac80\uc0c9\ub41c \uac1d\uccb4 \ucee8\ud14d\uc2a4\ud2b8\ub97c \uc0ac\uc6a9\ud574 \ub2f5\ubcc0\ud574\uc918"})),g&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},g)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(h.a,{color:"primary",variant:"contained",onClick:async()=>{b(""),s("");const e=(o||"").trim();if(!e)return void b("\uc9c8\ubb38\uc744 \uc785\ub825\ud558\uc138\uc694.");const n={userQuery:e,searchType:y,n_results:Number(C)||5};if(t&&(n.api_key=t),"image"===y){if(!a)return void b("\uc774\ubbf8\uc9c0\uac00 \ud544\uc694\ud569\ub2c8\ub2e4. \uba3c\uc800 \uc774\ubbf8\uc9c0\ub97c \uc5c5\ub85c\ub4dc\ud558\uc138\uc694.");n.image=a}else if("object"===y){if(!v.trim())return void b("objectId\ub97c \uc785\ub825\ud558\uc138\uc694.");n.objectId=v.trim()}else if("class"===y){if(!S.trim())return void b("class_name\uc744 \uc785\ub825\ud558\uc138\uc694.");n.class_name=S.trim()}d(!0);try{const e=await fetch("/api/vision-rag/query",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify(n)});if(!e.ok){let a=await e.text();try{a=JSON.stringify(JSON.parse(a),null,2)}catch(r){}throw new Error(a)}const a=await e.json(),t="Model: ".concat(a.model||"-"," | Latency: ").concat(a.latency_sec||"-","s");let l="";a.retrieved&&a.retrieved.length>0&&(l="\n\n\uac80\uc0c9 \uacb0\uacfc:\n",a.retrieved.forEach((e,a)=>{l+="".concat(a+1,". ID: ").concat(e.id||"-","\n"),e.meta&&(l+=" \ud074\ub798\uc2a4: ".concat(e.meta.class||"-","\n"),l+=" \uc2e0\ub8b0\ub3c4: ".concat(e.meta.confidence||"-","\n")),l+=" \uc720\uc0ac\ub3c4: ".concat(e.distance?e.distance.toFixed(4):"-","\n")})),s((a.answer||"(\ube48 \uc751\ub2f5)")+l+"\n\n---\n"+t)}catch(l){b("Error: "+l.message)}finally{d(!1)}},disabled:m},m?"Sending...":"Send Question"),r.a.createElement(h.a,{variant:"outlined",onClick:()=>{c(""),s(""),b("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(z.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},i))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(""),[h,y]=Object(n.useState)(!1),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)(null),[j,C]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{C(e.models)}).catch(e=>{console.error("Error checking API status:",e),S("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(w,{onImageUpload:e=>{if(a(e),v(null),S(null),e instanceof File){const a=new FileReader;a.onload=()=>{const e=a.result;l("string"===typeof e?e:"")},a.onerror=()=>l(""),a.readAsDataURL(e)}else l("")}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(_,{onModelSelect:e=>{c(e),v(null),S(null)},onProcess:async()=>{if(!e||!o)return void S("Please select both an image and a model");y(!0),S(null);const a=new FormData;a.append("image",e);let t="";switch(o){case"yolo":t="/api/detect/yolo";break;case"detr":t="/api/detect/detr";break;case"vit":t="/api/classify/vit";break;default:return S("Invalid model selection"),void y(!1)}try{const e=await fetch(t,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const n=await e.json();v({model:o,data:n})}catch(n){console.error("Error processing image:",n),S("Error processing image: ".concat(n.message))}finally{y(!1)}},isProcessing:h,modelsStatus:j,selectedModel:o,imageSelected:!!e})),x&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},x))),h&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(b.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),f&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:f})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:f.data,model:f.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,{imageBase64:t}))))))};var se=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
- //# sourceMappingURL=main.fe247ab2.chunk.js.map
 
 
 
static/static/js/main.fe247ab2.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","components/OpenAIChat.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","breakpoints","down","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","maxHeight","objectFit","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","detections","imageId","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","OpenAIChat","imageBase64","apiKey","setApiKey","prompt","setPrompt","setResponse","loading","setLoading","objectId","setObjectId","setClassName","nResults","setNResults","inputProps","min","max","multiline","rows","q","api_key","res","credentials","txt","text","parse","_unused","meta","latency_sec","retrievedText","retrieved","forEach","answer","onClear","fontFamily","createMuiTheme","typography","App","selectedImage","setSelectedImage","setImageBase64","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","File","reader","FileReader","onload","dataUrl","onerror","readAsDataURL","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.fe247ab2.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n // Use viewport-based height so any aspect ratio fits inside\n height: '60vh',\n [theme.breakpoints.down('sm')]: {\n height: '45vh',\n },\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2),\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n style={{\n width: '100%',\n height: 'auto',\n maxHeight: '100%',\n objectFit: 'contain',\n display: 'block'\n }}\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-to-collection', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.detections,\n imageId: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n searchType: 'image',\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n searchType: 'class',\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\n )}\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState } from 'react';\nimport {\n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider,\n MenuItem,\n Select,\n FormControl,\n InputLabel\n} from '@material-ui/core';\n\n// Props:\n// - imageBase64: data URL string of the currently uploaded image (preferred for searchType=image)\nfunction OpenAIChat({ imageBase64 }) {\n const [apiKey, setApiKey] = useState('');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n // Vision RAG parameters\n const [searchType, setSearchType] = useState('image');\n const [objectId, setObjectId] = useState('');\n const [className, setClassName] = useState('');\n const [nResults, setNResults] = useState(5);\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const q = (prompt || '').trim();\n if (!q) { setError('μ§ˆλ¬Έμ„ μž…λ ₯ν•˜μ„Έμš”.'); return; }\n\n // Build request body for /api/vision-rag/query\n const body = {\n userQuery: q,\n searchType: searchType,\n n_results: Number(nResults) || 5,\n };\n if (apiKey) body.api_key = apiKey;\n if (searchType === 'image') {\n if (!imageBase64) { setError('이미지가 ν•„μš”ν•©λ‹ˆλ‹€. λ¨Όμ € 이미지λ₯Ό μ—…λ‘œλ“œν•˜μ„Έμš”.'); return; }\n body.image = imageBase64;\n } else if (searchType === 'object') {\n if (!objectId.trim()) { setError('objectIdλ₯Ό μž…λ ₯ν•˜μ„Έμš”.'); return; }\n body.objectId = objectId.trim();\n } else if (searchType === 'class') {\n if (!className.trim()) { setError('class_name을 μž…λ ₯ν•˜μ„Έμš”.'); return; }\n body.class_name = className.trim();\n }\n\n setLoading(true);\n try {\n const res = await fetch('/api/vision-rag/query', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify(body)\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model || '-'} | Latency: ${data.latency_sec || '-'}s`;\n \n // 검색 κ²°κ³Ό ν¬λ§·νŒ…\n let retrievedText = '';\n if (data.retrieved && data.retrieved.length > 0) {\n retrievedText = '\\n\\n검색 κ²°κ³Ό:\\n';\n data.retrieved.forEach((item, index) => {\n retrievedText += `${index + 1}. ID: ${item.id || '-'}\\n`;\n if (item.meta) {\n retrievedText += ` 클래슀: ${item.meta.class || '-'}\\n`;\n retrievedText += ` 신뒰도: ${item.meta.confidence || '-'}\\n`;\n }\n retrievedText += ` μœ μ‚¬λ„: ${item.distance ? item.distance.toFixed(4) : '-'}\\n`;\n });\n }\n \n setResponse((data.answer || '(빈 응닡)') + retrievedText + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n Vision RAG (LangChain)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n μ„œλ²„μ— OPENAI_API_KEYκ°€ μ„€μ •λ˜μ–΄ μžˆλ‹€λ©΄ API KeyλŠ” μƒλž΅ κ°€λŠ₯ν•©λ‹ˆλ‹€. 검색 μœ ν˜•μ„ μ„ νƒν•˜κ³  μ§ˆλ¬Έμ„ 보내면, 벑터 DBμ—μ„œ κ²€μƒ‰λœ μ»¨ν…μŠ€νŠΈλ‘œ λ‹΅λ³€ν•©λ‹ˆλ‹€.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <FormControl fullWidth variant=\"outlined\" size=\"small\">\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n value={searchType}\n onChange={(e) => setSearchType(e.target.value)}\n label=\"Search Type\"\n >\n <MenuItem value=\"image\">image (current upload)</MenuItem>\n <MenuItem value=\"object\">object (objectId)</MenuItem>\n <MenuItem value=\"class\">class (class_name)</MenuItem>\n </Select>\n </FormControl>\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n\n {searchType === 'object' && (\n <Grid item xs={12} md={6}>\n <TextField\n label=\"objectId\"\n value={objectId}\n onChange={(e) => setObjectId(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n )}\n {searchType === 'class' && (\n <Grid item xs={12} md={6}>\n <TextField\n label=\"class_name\"\n value={className}\n onChange={(e) => setClassName(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n placeholder=\"e.g. person, car, dog\"\n />\n </Grid>\n )}\n <Grid item xs={12} md={6}>\n <TextField\n label=\"n_results\"\n value={nResults}\n onChange={(e) => setNResults(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"number\"\n inputProps={{ min: 1, max: 50 }}\n />\n </Grid>\n\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n placeholder={searchType === 'image' ? 'μ—…λ‘œλ“œν•œ 이미지 기반으둜 λ‹΅λ³€ν•΄μ€˜' : 'κ²€μƒ‰λœ 객체 μ»¨ν…μŠ€νŠΈλ₯Ό μ‚¬μš©ν•΄ λ‹΅λ³€ν•΄μ€˜'}\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport OpenAIChat from './components/OpenAIChat';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [imageBase64, setImageBase64] = useState('');\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n\n // Convert uploaded File -> base64 data URL for Vision RAG image search\n if (image instanceof File) {\n const reader = new FileReader();\n reader.onload = () => {\n const dataUrl = reader.result; // e.g., data:image/png;base64,....\n setImageBase64(typeof dataUrl === 'string' ? dataUrl : '');\n };\n reader.onerror = () => setImageBase64('');\n reader.readAsDataURL(image);\n } else {\n setImageBase64('');\n }\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n\n {/* Vision RAG (LangChain) section at the end */}\n <Grid item xs={12}>\n <OpenAIChat imageBase64={imageBase64} />\n </Grid>\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
 
 
static_bak_20250820_085943/asset-manifest.json DELETED
@@ -1,24 +0,0 @@
1
- {
2
- "files": {
3
- "main.css": "/static/css/main.3b4eede1.chunk.css",
4
- "main.js": "/static/js/main.fc0e2dc7.chunk.js",
5
- "main.js.map": "/static/js/main.fc0e2dc7.chunk.js.map",
6
- "runtime-main.js": "/static/js/runtime-main.25710301.js",
7
- "runtime-main.js.map": "/static/js/runtime-main.25710301.js.map",
8
- "static/js/2.9b5b80e8.chunk.js": "/static/js/2.9b5b80e8.chunk.js",
9
- "static/js/2.9b5b80e8.chunk.js.map": "/static/js/2.9b5b80e8.chunk.js.map",
10
- "static/js/3.9013e23f.chunk.js": "/static/js/3.9013e23f.chunk.js",
11
- "static/js/3.9013e23f.chunk.js.map": "/static/js/3.9013e23f.chunk.js.map",
12
- "index.html": "/index.html",
13
- "precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js": "/precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js",
14
- "service-worker.js": "/service-worker.js",
15
- "static/css/main.3b4eede1.chunk.css.map": "/static/css/main.3b4eede1.chunk.css.map",
16
- "static/js/2.9b5b80e8.chunk.js.LICENSE.txt": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
17
- },
18
- "entrypoints": [
19
- "static/js/runtime-main.25710301.js",
20
- "static/js/2.9b5b80e8.chunk.js",
21
- "static/css/main.3b4eede1.chunk.css",
22
- "static/js/main.fc0e2dc7.chunk.js"
23
- ]
24
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static_bak_20250820_085943/index.html DELETED
@@ -1 +0,0 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Multi-Model Object Detection Demo"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Vision Web App</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><link href="/static/css/main.3b4eede1.chunk.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"9013e23f"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([])</script><script src="/static/js/2.9b5b80e8.chunk.js"></script><script src="/static/js/main.fc0e2dc7.chunk.js"></script></body></html>
 
 
static_bak_20250820_085943/manifest.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "short_name": "Vision Web App",
3
- "name": "Multi-Model Object Detection Demo",
4
- "icons": [
5
- {
6
- "src": "favicon.ico",
7
- "sizes": "64x64 32x32 24x24 16x16",
8
- "type": "image/x-icon"
9
- }
10
- ],
11
- "start_url": ".",
12
- "display": "standalone",
13
- "theme_color": "#000000",
14
- "background_color": "#ffffff"
15
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static_bak_20250820_085943/precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js DELETED
@@ -1,30 +0,0 @@
1
- self.__precacheManifest = (self.__precacheManifest || []).concat([
2
- {
3
- "revision": "d54f7d4646b42d5859a3c60dc17c2cbc",
4
- "url": "/index.html"
5
- },
6
- {
7
- "revision": "bb5a62e98165b2cca977",
8
- "url": "/static/css/main.3b4eede1.chunk.css"
9
- },
10
- {
11
- "revision": "ca6d8d363680165da358",
12
- "url": "/static/js/2.9b5b80e8.chunk.js"
13
- },
14
- {
15
- "revision": "89a1b2dcd30c03705b2bceeb141b76b6",
16
- "url": "/static/js/2.9b5b80e8.chunk.js.LICENSE.txt"
17
- },
18
- {
19
- "revision": "70d68b45d511e1e11f23",
20
- "url": "/static/js/3.9013e23f.chunk.js"
21
- },
22
- {
23
- "revision": "bb5a62e98165b2cca977",
24
- "url": "/static/js/main.fc0e2dc7.chunk.js"
25
- },
26
- {
27
- "revision": "d8c310b0ac7ffa6d8151",
28
- "url": "/static/js/runtime-main.25710301.js"
29
- }
30
- ]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static_bak_20250820_085943/service-worker.js DELETED
@@ -1,39 +0,0 @@
1
- /**
2
- * Welcome to your Workbox-powered service worker!
3
- *
4
- * You'll need to register this file in your web app and you should
5
- * disable HTTP caching for this file too.
6
- * See https://goo.gl/nhQhGp
7
- *
8
- * The rest of the code is auto-generated. Please don't update this file
9
- * directly; instead, make changes to your Workbox build configuration
10
- * and re-run your build process.
11
- * See https://goo.gl/2aRDsh
12
- */
13
-
14
- importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
15
-
16
- importScripts(
17
- "/precache-manifest.4683f6cd857bd9aa85304ac13a82d061.js"
18
- );
19
-
20
- self.addEventListener('message', (event) => {
21
- if (event.data && event.data.type === 'SKIP_WAITING') {
22
- self.skipWaiting();
23
- }
24
- });
25
-
26
- workbox.core.clientsClaim();
27
-
28
- /**
29
- * The workboxSW.precacheAndRoute() method efficiently caches and responds to
30
- * requests for URLs in the manifest.
31
- * See https://goo.gl/S9QRab
32
- */
33
- self.__precacheManifest = [].concat(self.__precacheManifest || []);
34
- workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
35
-
36
- workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
37
-
38
- blacklist: [/^\/_/,/\/[^\/?]+\.[^\/]+$/],
39
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static_bak_20250820_085943/static/css/main.3b4eede1.chunk.css DELETED
@@ -1,2 +0,0 @@
1
- body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:#f5f5f5}code{font-family:source-code-pro,Menlo,Monaco,Consolas,"Courier New",monospace}.App{text-align:center}.preview-image{width:100%;height:auto;max-height:100%;object-fit:contain;display:block;margin-top:0}.result-image{max-width:100%;border:1px solid #ddd;border-radius:4px;padding:4px}.detection-list{margin-top:16px;text-align:left}.model-card{cursor:pointer;transition:all .3s}.model-card:hover{transform:translateY(-5px);box-shadow:0 10px 20px rgba(0,0,0,.1)}.model-card.selected{border:2px solid #3f51b5;background-color:#e8eaf6}.model-card.disabled{opacity:.6;cursor:not-allowed}.performance-info{margin-top:16px;font-size:.9rem;color:#666}
2
- /*# sourceMappingURL=main.3b4eede1.chunk.css.map */
 
 
 
static_bak_20250820_085943/static/css/main.3b4eede1.chunk.css.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["index.css","App.css"],"names":[],"mappings":"AAAA,KACE,QAAS,CACT,mJAEY,CACZ,kCAAmC,CACnC,iCAAkC,CAClC,wBACF,CAEA,KACE,yEAEF,CCbA,KACE,iBACF,CAEA,eACE,UAAW,CACX,WAAY,CACZ,eAAgB,CAChB,kBAAmB,CACnB,aAAc,CACd,YACF,CAEA,cACE,cAAe,CACf,qBAAsB,CACtB,iBAAkB,CAClB,WACF,CAEA,gBACE,eAAgB,CAChB,eACF,CAEA,YACE,cAAe,CACf,kBACF,CAEA,kBACE,0BAA2B,CAC3B,qCACF,CAEA,qBACE,wBAAyB,CACzB,wBACF,CAEA,qBACE,UAAY,CACZ,kBACF,CAEA,kBACE,eAAgB,CAChB,eAAiB,CACjB,UACF","file":"main.3b4eede1.chunk.css","sourcesContent":["body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n background-color: #f5f5f5;\n}\n\ncode {\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\n monospace;\n}\n",".App {\n text-align: center;\n}\n\n.preview-image {\n width: 100%;\n height: auto;\n max-height: 100%;\n object-fit: contain;\n display: block;\n margin-top: 0;\n}\n\n.result-image {\n max-width: 100%;\n border: 1px solid #ddd;\n border-radius: 4px;\n padding: 4px;\n}\n\n.detection-list {\n margin-top: 16px;\n text-align: left;\n}\n\n.model-card {\n cursor: pointer;\n transition: all 0.3s;\n}\n\n.model-card:hover {\n transform: translateY(-5px);\n box-shadow: 0 10px 20px rgba(0,0,0,0.1);\n}\n\n.model-card.selected {\n border: 2px solid #3f51b5;\n background-color: #e8eaf6;\n}\n\n.model-card.disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.performance-info {\n margin-top: 16px;\n font-size: 0.9rem;\n color: #666;\n}\n"]}
 
 
static_bak_20250820_085943/static/js/2.9b5b80e8.chunk.js DELETED
The diff for this file is too large to render. See raw diff
 
static_bak_20250820_085943/static/js/2.9b5b80e8.chunk.js.LICENSE.txt DELETED
@@ -1,58 +0,0 @@
1
- /*
2
- object-assign
3
- (c) Sindre Sorhus
4
- @license MIT
5
- */
6
-
7
- /**
8
- * A better abstraction over CSS.
9
- *
10
- * @copyright Oleg Isonen (Slobodskoi) / Isonen 2014-present
11
- * @website https://github.com/cssinjs/jss
12
- * @license MIT
13
- */
14
-
15
- /** @license React v0.19.1
16
- * scheduler.production.min.js
17
- *
18
- * Copyright (c) Facebook, Inc. and its affiliates.
19
- *
20
- * This source code is licensed under the MIT license found in the
21
- * LICENSE file in the root directory of this source tree.
22
- */
23
-
24
- /** @license React v16.13.1
25
- * react-is.production.min.js
26
- *
27
- * Copyright (c) Facebook, Inc. and its affiliates.
28
- *
29
- * This source code is licensed under the MIT license found in the
30
- * LICENSE file in the root directory of this source tree.
31
- */
32
-
33
- /** @license React v16.14.0
34
- * react-dom.production.min.js
35
- *
36
- * Copyright (c) Facebook, Inc. and its affiliates.
37
- *
38
- * This source code is licensed under the MIT license found in the
39
- * LICENSE file in the root directory of this source tree.
40
- */
41
-
42
- /** @license React v16.14.0
43
- * react.production.min.js
44
- *
45
- * Copyright (c) Facebook, Inc. and its affiliates.
46
- *
47
- * This source code is licensed under the MIT license found in the
48
- * LICENSE file in the root directory of this source tree.
49
- */
50
-
51
- /** @license React v17.0.2
52
- * react-is.production.min.js
53
- *
54
- * Copyright (c) Facebook, Inc. and its affiliates.
55
- *
56
- * This source code is licensed under the MIT license found in the
57
- * LICENSE file in the root directory of this source tree.
58
- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static_bak_20250820_085943/static/js/2.9b5b80e8.chunk.js.map DELETED
The diff for this file is too large to render. See raw diff
 
static_bak_20250820_085943/static/js/3.9013e23f.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[3],{170:function(t,n,e){"use strict";e.r(n),e.d(n,"getCLS",(function(){return l})),e.d(n,"getFCP",(function(){return g})),e.d(n,"getFID",(function(){return h})),e.d(n,"getLCP",(function(){return y})),e.d(n,"getTTFB",(function(){return F}));var i,a,r=function(){return"".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12)},o=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:n,delta:0,entries:[],id:r(),isFinal:!1}},u=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},s=!1,c=!1,p=function(t){s=!t.persisted},d=function(){addEventListener("pagehide",p),addEventListener("beforeunload",(function(){}))},f=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];c||(d(),c=!0),addEventListener("visibilitychange",(function(n){var e=n.timeStamp;"hidden"===document.visibilityState&&t({timeStamp:e,isUnloading:s})}),{capture:!0,once:n})},v=function(t,n,e,i){var a;return function(){e&&n.isFinal&&e.disconnect(),n.value>=0&&(i||n.isFinal||"hidden"===document.visibilityState)&&(n.delta=n.value-(a||0),(n.delta||n.isFinal||void 0===a)&&(t(n),a=n.value))}},l=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("CLS",0),a=function(t){t.hadRecentInput||(i.value+=t.value,i.entries.push(t),n())},r=u("layout-shift",a);r&&(n=v(t,i,r,e),f((function(t){var e=t.isUnloading;r.takeRecords().map(a),e&&(i.isFinal=!0),n()})))},m=function(){return void 0===i&&(i="hidden"===document.visibilityState?0:1/0,f((function(t){var n=t.timeStamp;return i=n}),!0)),{get timeStamp(){return i}}},g=function(t){var n,e=o("FCP"),i=m(),a=u("paint",(function(t){"first-contentful-paint"===t.name&&t.startTime<i.timeStamp&&(e.value=t.startTime,e.isFinal=!0,e.entries.push(t),n())}));a&&(n=v(t,e,a))},h=function(t){var n=o("FID"),e=m(),i=function(t){t.startTime<e.timeStamp&&(n.value=t.processingStart-t.startTime,n.entries.push(t),n.isFinal=!0,r())},a=u("first-input",i),r=v(t,n,a);a?f((function(){a.takeRecords().map(i),a.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<e.timeStamp&&(n.value=t,n.isFinal=!0,n.entries=[{entryType:"first-input",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],r())}))},S=function(){return a||(a=new Promise((function(t){return["scroll","keydown","pointerdown"].map((function(n){addEventListener(n,t,{once:!0,passive:!0,capture:!0})}))}))),a},y=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=o("LCP"),a=m(),r=function(t){var e=t.startTime;e<a.timeStamp?(i.value=e,i.entries.push(t)):i.isFinal=!0,n()},s=u("largest-contentful-paint",r);if(s){n=v(t,i,s,e);var c=function(){i.isFinal||(s.takeRecords().map(r),i.isFinal=!0,n())};S().then(c),f(c,!0)}},F=function(t){var n,e=o("TTFB");n=function(){try{var n=performance.getEntriesByType("navigation")[0]||function(){var t=performance.timing,n={entryType:"navigation",startTime:0};for(var e in t)"navigationStart"!==e&&"toJSON"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],e.isFinal=!0,t(e)}catch(t){}},"complete"===document.readyState?setTimeout(n,0):addEventListener("pageshow",n)}}}]);
2
- //# sourceMappingURL=3.9013e23f.chunk.js.map
 
 
 
static_bak_20250820_085943/static/js/3.9013e23f.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../node_modules/web-vitals/dist/web-vitals.es5.min.js"],"names":["v","t","n","e","concat","Date","now","Math","floor","random","i","arguments","length","name","value","delta","entries","id","isFinal","a","PerformanceObserver","supportedEntryTypes","includes","getEntries","map","observe","type","buffered","r","o","s","persisted","u","addEventListener","c","timeStamp","document","visibilityState","isUnloading","capture","once","l","disconnect","p","hadRecentInput","push","takeRecords","d","startTime","f","processingStart","window","perfMetrics","onFirstInputDelay","entryType","target","cancelable","m","Promise","passive","g","then","h","performance","getEntriesByType","timing","max","navigationStart","responseStart","readyState","setTimeout"],"mappings":"wHAAA,gFAAAA,KAAA,0HAAIC,EAAEC,EAAEC,EAAE,WAAW,MAAM,GAAGC,OAAOC,KAAKC,MAAM,KAAKF,OAAOG,KAAKC,MAAM,cAAcD,KAAKE,UAAU,OAAOC,EAAE,SAAST,GAAG,IAAIC,EAAES,UAAUC,OAAO,QAAG,IAASD,UAAU,GAAGA,UAAU,IAAI,EAAE,MAAM,CAACE,KAAKZ,EAAEa,MAAMZ,EAAEa,MAAM,EAAEC,QAAQ,GAAGC,GAAGd,IAAIe,SAAQ,IAAKC,EAAE,SAASlB,EAAEC,GAAG,IAAI,GAAGkB,oBAAoBC,oBAAoBC,SAASrB,GAAG,CAAC,IAAIE,EAAE,IAAIiB,qBAAqB,SAASnB,GAAG,OAAOA,EAAEsB,aAAaC,IAAItB,MAAM,OAAOC,EAAEsB,QAAQ,CAACC,KAAKzB,EAAE0B,UAAS,IAAKxB,GAAG,MAAMF,MAAM2B,GAAE,EAAGC,GAAE,EAAGC,EAAE,SAAS7B,GAAG2B,GAAG3B,EAAE8B,WAAWC,EAAE,WAAWC,iBAAiB,WAAWH,GAAGG,iBAAiB,gBAAgB,gBAAgBC,EAAE,SAASjC,GAAG,IAAIC,EAAES,UAAUC,OAAO,QAAG,IAASD,UAAU,IAAIA,UAAU,GAAGkB,IAAIG,IAAIH,GAAE,GAAII,iBAAiB,oBAAoB,SAAS/B,GAAG,IAAIC,EAAED,EAAEiC,UAAU,WAAWC,SAASC,iBAAiBpC,EAAE,CAACkC,UAAUhC,EAAEmC,YAAYV,MAAM,CAACW,SAAQ,EAAGC,KAAKtC,KAAKuC,EAAE,SAASxC,EAAEC,EAAEC,EAAEO,GAAG,IAAIS,EAAE,OAAO,WAAWhB,GAAGD,EAAEgB,SAASf,EAAEuC,aAAaxC,EAAEY,OAAO,IAAIJ,GAAGR,EAAEgB,SAAS,WAAWkB,SAASC,mBAAmBnC,EAAEa,MAAMb,EAAEY,OAAOK,GAAG,IAAIjB,EAAEa,OAAOb,EAAEgB,cAAS,IAASC,KAAKlB,EAAEC,GAAGiB,EAAEjB,EAAEY,UAAU6B,EAAE,SAAS1C,GAAG,IAAIC,EAAEC,EAAEQ,UAAUC,OAAO,QAAG,IAASD,UAAU,IAAIA,UAAU,GAAGiB,EAAElB,EAAE,MAAM,GAAGmB,EAAE,SAAS5B,GAAGA,EAAE2C,iBAAiBhB,EAAEd,OAAOb,EAAEa,MAAMc,EAAEZ,QAAQ6B,KAAK5C,GAAGC,MAAM4B,EAAEX,EAAE,eAAeU,GAAGC,IAAI5B,EAAEuC,EAAExC,EAAE2B,EAAEE,EAAE3B,GAAG+B,GAAG,SAASjC,GAAG,IAAIE,EAAEF,EAAEqC,YAAYR,EAAEgB,cAActB,IAAIK,GAAG1B,IAAIyB,EAAEV,SAAQ,GAAIhB,SAAS6C,EAAE,WAAW,YAAO,IAAS9C,IAAIA,EAAE,WAAWmC,SAASC,gBAAgB,EAAE,IAAIH,GAAG,SAAShC,GAAG,IAAIC,EAAED,EAAEiC,UAAU,OAAOlC,EAAEE,KAAI,IAAK,CAAC,gBAAgB,OAAOF,KAAKD,EAAE,SAASC,GAAG,IAAIC,EAAEC,EAAEO,EAAE,OAAOkB,EAAEmB,IAAIlB,EAAEV,EAAE,SAAS,SAASlB,GAAG,2BAA2BA,EAAEY,MAAMZ,EAAE+C,UAAUpB,EAAEO,YAAYhC,EAAEW,MAAMb,EAAE+C,UAAU7C,EAAEe,SAAQ,EAAGf,EAAEa,QAAQ6B,KAAK5C,GAAGC,QAAQ2B,IAAI3B,EAAEuC,EAAExC,EAAEE,EAAE0B,KAAKoB,EAAE,SAAShD,GAAG,IAAIC,EAAEQ,EAAE,OAAOP,EAAE4C,IAAInB,EAAE,SAAS3B,GAAGA,EAAE+C,UAAU7C,EAAEgC,YAAYjC,EAAEY,MAAMb,EAAEiD,gBAAgBjD,EAAE+C,UAAU9C,EAAEc,QAAQ6B,KAAK5C,GAAGC,EAAEgB,SAAQ,EAAGY,MAAMD,EAAEV,EAAE,cAAcS,GAAGE,EAAEW,EAAExC,EAAEC,EAAE2B,GAAGA,EAAEK,GAAG,WAAWL,EAAEiB,cAActB,IAAII,GAAGC,EAAEa,gBAAe,GAAIS,OAAOC,aAAaD,OAAOC,YAAYC,mBAAmBF,OAAOC,YAAYC,mBAAmB,SAASpD,EAAES,GAAGA,EAAEyB,UAAUhC,EAAEgC,YAAYjC,EAAEY,MAAMb,EAAEC,EAAEgB,SAAQ,EAAGhB,EAAEc,QAAQ,CAAC,CAACsC,UAAU,cAAczC,KAAKH,EAAEgB,KAAK6B,OAAO7C,EAAE6C,OAAOC,WAAW9C,EAAE8C,WAAWR,UAAUtC,EAAEyB,UAAUe,gBAAgBxC,EAAEyB,UAAUlC,IAAI6B,SAAS2B,EAAE,WAAW,OAAOvD,IAAIA,EAAE,IAAIwD,SAAS,SAASzD,GAAG,MAAM,CAAC,SAAS,UAAU,eAAeuB,KAAK,SAAStB,GAAG+B,iBAAiB/B,EAAED,EAAE,CAACuC,MAAK,EAAGmB,SAAQ,EAAGpB,SAAQ,WAAYrC,GAAG0D,EAAE,SAAS3D,GAAG,IAAIC,EAAEC,EAAEQ,UAAUC,OAAO,QAAG,IAASD,UAAU,IAAIA,UAAU,GAAGiB,EAAElB,EAAE,OAAOmB,EAAEkB,IAAIjB,EAAE,SAAS7B,GAAG,IAAIE,EAAEF,EAAE+C,UAAU7C,EAAE0B,EAAEM,WAAWP,EAAEd,MAAMX,EAAEyB,EAAEZ,QAAQ6B,KAAK5C,IAAI2B,EAAEV,SAAQ,EAAGhB,KAAK8B,EAAEb,EAAE,2BAA2BW,GAAG,GAAGE,EAAE,CAAC9B,EAAEuC,EAAExC,EAAE2B,EAAEI,EAAE7B,GAAG,IAAIwC,EAAE,WAAWf,EAAEV,UAAUc,EAAEc,cAActB,IAAIM,GAAGF,EAAEV,SAAQ,EAAGhB,MAAMuD,IAAII,KAAKlB,GAAGT,EAAES,GAAE,KAAMmB,EAAE,SAAS7D,GAAG,IAAIC,EAAEC,EAAEO,EAAE,QAAQR,EAAE,WAAW,IAAI,IAAIA,EAAE6D,YAAYC,iBAAiB,cAAc,IAAI,WAAW,IAAI/D,EAAE8D,YAAYE,OAAO/D,EAAE,CAACoD,UAAU,aAAaN,UAAU,GAAG,IAAI,IAAI7C,KAAKF,EAAE,oBAAoBE,GAAG,WAAWA,IAAID,EAAEC,GAAGI,KAAK2D,IAAIjE,EAAEE,GAAGF,EAAEkE,gBAAgB,IAAI,OAAOjE,EAAhL,GAAqLC,EAAEW,MAAMX,EAAEY,MAAMb,EAAEkE,cAAcjE,EAAEa,QAAQ,CAACd,GAAGC,EAAEe,SAAQ,EAAGjB,EAAEE,GAAG,MAAMF,MAAM,aAAamC,SAASiC,WAAWC,WAAWpE,EAAE,GAAG+B,iBAAiB,WAAW/B","file":"static/js/3.9013e23f.chunk.js","sourcesContent":["var t,n,e=function(){return\"\".concat(Date.now(),\"-\").concat(Math.floor(8999999999999*Math.random())+1e12)},i=function(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:-1;return{name:t,value:n,delta:0,entries:[],id:e(),isFinal:!1}},a=function(t,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var e=new PerformanceObserver((function(t){return t.getEntries().map(n)}));return e.observe({type:t,buffered:!0}),e}}catch(t){}},r=!1,o=!1,s=function(t){r=!t.persisted},u=function(){addEventListener(\"pagehide\",s),addEventListener(\"beforeunload\",(function(){}))},c=function(t){var n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];o||(u(),o=!0),addEventListener(\"visibilitychange\",(function(n){var e=n.timeStamp;\"hidden\"===document.visibilityState&&t({timeStamp:e,isUnloading:r})}),{capture:!0,once:n})},l=function(t,n,e,i){var a;return function(){e&&n.isFinal&&e.disconnect(),n.value>=0&&(i||n.isFinal||\"hidden\"===document.visibilityState)&&(n.delta=n.value-(a||0),(n.delta||n.isFinal||void 0===a)&&(t(n),a=n.value))}},p=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=i(\"CLS\",0),o=function(t){t.hadRecentInput||(r.value+=t.value,r.entries.push(t),n())},s=a(\"layout-shift\",o);s&&(n=l(t,r,s,e),c((function(t){var e=t.isUnloading;s.takeRecords().map(o),e&&(r.isFinal=!0),n()})))},d=function(){return void 0===t&&(t=\"hidden\"===document.visibilityState?0:1/0,c((function(n){var e=n.timeStamp;return t=e}),!0)),{get timeStamp(){return t}}},v=function(t){var n,e=i(\"FCP\"),r=d(),o=a(\"paint\",(function(t){\"first-contentful-paint\"===t.name&&t.startTime<r.timeStamp&&(e.value=t.startTime,e.isFinal=!0,e.entries.push(t),n())}));o&&(n=l(t,e,o))},f=function(t){var n=i(\"FID\"),e=d(),r=function(t){t.startTime<e.timeStamp&&(n.value=t.processingStart-t.startTime,n.entries.push(t),n.isFinal=!0,s())},o=a(\"first-input\",r),s=l(t,n,o);o?c((function(){o.takeRecords().map(r),o.disconnect()}),!0):window.perfMetrics&&window.perfMetrics.onFirstInputDelay&&window.perfMetrics.onFirstInputDelay((function(t,i){i.timeStamp<e.timeStamp&&(n.value=t,n.isFinal=!0,n.entries=[{entryType:\"first-input\",name:i.type,target:i.target,cancelable:i.cancelable,startTime:i.timeStamp,processingStart:i.timeStamp+t}],s())}))},m=function(){return n||(n=new Promise((function(t){return[\"scroll\",\"keydown\",\"pointerdown\"].map((function(n){addEventListener(n,t,{once:!0,passive:!0,capture:!0})}))}))),n},g=function(t){var n,e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=i(\"LCP\"),o=d(),s=function(t){var e=t.startTime;e<o.timeStamp?(r.value=e,r.entries.push(t)):r.isFinal=!0,n()},u=a(\"largest-contentful-paint\",s);if(u){n=l(t,r,u,e);var p=function(){r.isFinal||(u.takeRecords().map(s),r.isFinal=!0,n())};m().then(p),c(p,!0)}},h=function(t){var n,e=i(\"TTFB\");n=function(){try{var n=performance.getEntriesByType(\"navigation\")[0]||function(){var t=performance.timing,n={entryType:\"navigation\",startTime:0};for(var e in t)\"navigationStart\"!==e&&\"toJSON\"!==e&&(n[e]=Math.max(t[e]-t.navigationStart,0));return n}();e.value=e.delta=n.responseStart,e.entries=[n],e.isFinal=!0,t(e)}catch(t){}},\"complete\"===document.readyState?setTimeout(n,0):addEventListener(\"pageshow\",n)};export{p as getCLS,v as getFCP,f as getFID,g as getLCP,h as getTTFB};\n"],"sourceRoot":""}
 
 
static_bak_20250820_085943/static/js/main.fc0e2dc7.chunk.js DELETED
@@ -1,2 +0,0 @@
1
- (this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[]).push([[0],{102:function(e,a,t){},103:function(e,a,t){"use strict";t.r(a);var n=t(0),r=t.n(n),l=t(11),o=t.n(l),c=(t(97),t(78)),i=t(155),s=t(159),m=t(156),d=t(157),u=t(48),g=t(158),p=t(138),E=t(81),h=t(143),y=t(136),b=t(137),f=t(63),v=t.n(f),x=t(75),S=t.n(x),C=t(133);const j=Object(C.a)(e=>({paper:{padding:e.spacing(2),display:"flex",flexDirection:"column",alignItems:"center",height:"100%",minHeight:300,transition:"all 0.3s ease"},dragActive:{border:"2px dashed #3f51b5",backgroundColor:"rgba(63, 81, 181, 0.05)"},dragInactive:{border:"2px dashed #ccc",backgroundColor:"white"},uploadBox:{display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",height:"100%",width:"100%",cursor:"pointer"},uploadIcon:{fontSize:60,color:"#3f51b5",marginBottom:e.spacing(2)},supportText:{marginTop:e.spacing(2)},previewBox:{display:"flex",flexDirection:"column",alignItems:"center",width:"100%",height:"100%",position:"relative"},imageContainer:{position:"relative",width:"100%",height:"60vh",[e.breakpoints.down("sm")]:{height:"45vh"},display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",marginTop:e.spacing(2)},deleteButton:{position:"absolute",top:0,right:0,backgroundColor:"rgba(255, 255, 255, 0.7)","&:hover":{backgroundColor:"rgba(255, 255, 255, 0.9)"}}}));var w=e=>{let{onImageUpload:a}=e;const t=j(),[l,o]=Object(n.useState)(null),[c,i]=Object(n.useState)(!1),m=Object(n.useRef)(null),d=e=>{e.preventDefault(),e.stopPropagation(),"dragenter"===e.type||"dragover"===e.type?i(!0):"dragleave"===e.type&&i(!1)},g=e=>{e.type.startsWith("image/")?(o(URL.createObjectURL(e)),a(e)):alert("Please upload an image file")};return r.a.createElement(E.a,{className:"".concat(t.paper," ").concat(c?t.dragActive:t.dragInactive),onDragEnter:d,onDragLeave:d,onDragOver:d,onDrop:e=>{e.preventDefault(),e.stopPropagation(),i(!1),e.dataTransfer.files&&e.dataTransfer.files[0]&&g(e.dataTransfer.files[0])}},r.a.createElement("input",{ref:m,type:"file",accept:"image/*",onChange:e=>{e.preventDefault(),e.target.files&&e.target.files[0]&&g(e.target.files[0])},style:{display:"none"}}),l?r.a.createElement(s.a,{className:t.previewBox},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Preview"),r.a.createElement(s.a,{className:t.imageContainer},r.a.createElement("img",{src:l,alt:"Preview",className:"preview-image",style:{width:"100%",height:"auto",maxHeight:"100%",objectFit:"contain",display:"block"}}),r.a.createElement(b.a,{"aria-label":"delete",className:t.deleteButton,onClick:()=>{o(null),a(null),m.current.value=""}},r.a.createElement(S.a,null)))):r.a.createElement(s.a,{className:t.uploadBox,onClick:()=>{m.current.click()}},r.a.createElement(v.a,{className:t.uploadIcon}),r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Drag & Drop an image here"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"or"),r.a.createElement(y.a,{variant:"contained",color:"primary",component:"span",startIcon:r.a.createElement(v.a,null)},"Browse Files"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",className:t.supportText},"Supported formats: JPG, PNG, GIF")))},O=t(139),N=t(140),B=t(166),T=t(141),I=t(64),k=t.n(I),P=t(76),D=t.n(P),A=t(77),F=t.n(A);const R=Object(C.a)(e=>({card:{height:"100%",display:"flex",flexDirection:"column"},selectedCard:{border:"2px solid #3f51b5"},unavailableCard:{opacity:.6},cardContent:{flexGrow:1},chipContainer:{marginBottom:e.spacing(1.5)},successChip:{backgroundColor:"#34C759",color:"#fff"},errorChip:{backgroundColor:"#FF3B3F",color:"#fff"},modelType:{marginTop:e.spacing(1)},processButton:{marginTop:e.spacing(3),textAlign:"center"}}));var z=e=>{let{onModelSelect:a,onProcess:t,isProcessing:n,modelsStatus:l,selectedModel:o,imageSelected:c}=e;const i=R(),m=[{id:"yolo",name:"YOLOv8",description:"Fast and accurate object detection",icon:r.a.createElement(k.a,null),available:l.yolo},{id:"detr",name:"DETR",description:"DEtection TRansformer for object detection",icon:r.a.createElement(k.a,null),available:l.detr},{id:"vit",name:"ViT",description:"Vision Transformer for image classification",icon:r.a.createElement(D.a,null),available:l.vit}],d=e=>{m.find(a=>a.id===e).available&&a(e)};return r.a.createElement(s.a,{sx:{p:2,height:"100%"}},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Select Model"),r.a.createElement(p.a,{container:!0,spacing:2},m.map(e=>r.a.createElement(p.a,{item:!0,xs:12,sm:4,key:e.id},r.a.createElement(O.a,{className:"\n ".concat(i.card," \n ").concat(o===e.id?i.selectedCard:""," \n ").concat(e.available?"":i.unavailableCard,"\n "),onClick:()=>d(e.id)},r.a.createElement(N.a,{className:i.cardContent},r.a.createElement(s.a,{sx:{mb:2,color:"primary"}},e.icon),r.a.createElement(u.a,{variant:"h5",component:"div",gutterBottom:!0},e.name),r.a.createElement("div",{className:i.chipContainer},e.available?r.a.createElement(B.a,{label:"Available",className:i.successChip,size:"small"}):r.a.createElement(B.a,{label:"Not Available",className:i.errorChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.description)),r.a.createElement(T.a,null,r.a.createElement(y.a,{size:"small",onClick:()=>d(e.id),disabled:!e.available,color:o===e.id?"primary":"default",variant:o===e.id?"contained":"outlined",fullWidth:!0},o===e.id?"Selected":"Select")))))),r.a.createElement("div",{className:i.processButton},r.a.createElement(y.a,{variant:"contained",color:"primary",size:"large",startIcon:r.a.createElement(F.a,null),onClick:t,disabled:!o||!c||n},n?"Processing...":"Process Image")))},L=t(153),M=t(150),W=t(105),_=t(154),J=t(142),H=t(164),U=t(163),V=t(145),G=t(146),Y=t(147),q=t(167),K=t(160),Q=t(151),X=t(169),Z=t(152),$=t(161);const ee=Object(C.a)(e=>({root:{marginTop:e.spacing(2),marginBottom:e.spacing(2),padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius},button:{marginRight:e.spacing(2)},searchDialog:{minWidth:"500px"},formControl:{marginBottom:e.spacing(2),minWidth:"100%"},searchResults:{marginTop:e.spacing(2)},resultCard:{marginBottom:e.spacing(2)},resultImage:{height:140,objectFit:"contain"},chip:{margin:e.spacing(.5)},similarityChip:{backgroundColor:e.palette.primary.main,color:"white"}}));var ae=e=>{let{results:a}=e;const t=ee(),[l,o]=Object(n.useState)(!1),[c,i]=Object(n.useState)(!1),[m,d]=Object(n.useState)(null),[g,E]=Object(n.useState)(!1),[b,f]=Object(n.useState)("image"),[v,x]=Object(n.useState)(""),[S,C]=Object(n.useState)([]),[j,w]=Object(n.useState)(!1),[T,I]=Object(n.useState)(null),{model:k,data:P}=a,D=()=>{E(!1)},A=()=>"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(e){const a=16*Math.random()|0;return("x"===e?a:3&a|8).toString(16)}));return r.a.createElement(s.a,{className:t.root},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Vector Database Actions"),r.a.createElement(s.a,{display:"flex",alignItems:"center",mb:2},r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{o(!0),d(null);try{let e;if(e="vit"===k?await fetch("/api/add-to-collection",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,metadata:{model:"vit",classifications:P.classifications}})}):await fetch("/api/add-detected-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({image:P.image,objects:P.detections,imageId:A()})}),!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const a=await e.json();if(a.error)throw new Error(a.error);i(!0),setTimeout(()=>i(!1),5e3)}catch(e){console.error("Error saving to vector DB:",e),d("Error saving to vector DB: ".concat(e.message))}finally{o(!1)}},disabled:l,className:t.button},l?r.a.createElement(r.a.Fragment,null,r.a.createElement(h.a,{size:20,color:"inherit",style:{marginRight:8}}),"Saving..."):"Save to Vector DB"),r.a.createElement(y.a,{variant:"outlined",color:"primary",onClick:()=>{E(!0),C([]),I(null)},className:t.button},"Search Similar")),m&&r.a.createElement($.a,{severity:"error",style:{marginTop:8}},m),r.a.createElement(H.a,{open:c,autoHideDuration:5e3,onClose:()=>i(!1)},r.a.createElement($.a,{severity:"success"},"vit"===k?"Image and classifications successfully saved to vector DB!":"Detected objects successfully saved to vector DB!")),r.a.createElement(U.a,{open:g,onClose:D,maxWidth:"md",fullWidth:!0},r.a.createElement(V.a,null,"Search Vector Database"),r.a.createElement(G.a,null,r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(q.a,{id:"search-type-label"},"Search Type"),r.a.createElement(K.a,{labelId:"search-type-label",id:"search-type",value:b,onChange:e=>{f(e.target.value),C([]),I(null)}},r.a.createElement(Q.a,{value:"image"},"Search by Current Image"),r.a.createElement(Q.a,{value:"class"},"Search by Class Name"))),"class"===b&&r.a.createElement(Y.a,{className:t.formControl},r.a.createElement(X.a,{label:"Class Name",value:v,onChange:e=>{x(e.target.value)},placeholder:"e.g. person, car, dog...",fullWidth:!0})),T&&r.a.createElement($.a,{severity:"error",style:{marginBottom:16}},T),r.a.createElement(s.a,{className:t.searchResults},j?r.a.createElement(s.a,{display:"flex",justifyContent:"center",alignItems:"center",p:4},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"body1",style:{marginLeft:16}},"Searching...")):r.a.createElement(r.a.Fragment,null,console.log("Search dialog render - searchResults:",S),S.length>0?(console.log("Rendering search results:",S),console.log("Search results length:",S.length),0===S.length?(console.log("No results to render"),r.a.createElement(u.a,{variant:"body1"},"No results found.")):r.a.createElement(p.a,{container:!0,spacing:2},S.map((e,a)=>{const n=100*(1-e.distance);return r.a.createElement(p.a,{item:!0,xs:12,sm:6,key:a},r.a.createElement(O.a,{className:t.resultCard},e.metadata&&e.metadata.image_data?r.a.createElement(J.a,{className:t.resultImage,component:"img",height:"200",image:"data:image/jpeg;base64,".concat(e.metadata.image_data),alt:e.metadata&&e.metadata.class?e.metadata.class:"Object"}):r.a.createElement(s.a,{className:t.resultImage,style:{backgroundColor:"#f0f0f0",display:"flex",alignItems:"center",justifyContent:"center",height:200}},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},e.metadata&&e.metadata.class?e.metadata.class:"Object"," Image")),r.a.createElement(N.a,null,r.a.createElement(s.a,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1},r.a.createElement(u.a,{variant:"subtitle1"},"Result #",a+1),r.a.createElement(B.a,{label:"Similarity: ".concat(n.toFixed(2),"%"),className:t.similarityChip,size:"small"})),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Class:")," ",e.metadata.class||"N/A"),e.metadata.confidence&&r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Confidence:")," ",(100*e.metadata.confidence).toFixed(2),"%"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},r.a.createElement("strong",null,"Object ID:")," ",e.id))))}))):r.a.createElement(u.a,{variant:"body1"},"No results found. Please try another search.")))),r.a.createElement(Z.a,null,r.a.createElement(y.a,{onClick:D,color:"default"},"Close"),r.a.createElement(y.a,{onClick:async()=>{w(!0),I(null);try{let e={};if("image"===b)e={searchType:"image",image:P.image,n_results:5};else{if(!v.trim())throw new Error("Please enter a class name");e={searchType:"class",class_name:v.trim(),n_results:5}}const a=await fetch("/api/search-similar-objects",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!a.ok)throw new Error("HTTP error! Status: ".concat(a.status));const t=await a.json();if(t.error)throw new Error(t.error);if(console.log("Search API response:",t),!t.success||!Array.isArray(t.results))throw console.error("Unexpected API response format:",t),new Error("Unexpected API response format");console.log("Setting search results array:",t.results),console.log("Results array length:",t.results.length),console.log("First result item:",t.results[0]),C(t.results)}catch(e){console.error("Error searching vector DB:",e),I("Error searching vector DB: ".concat(e.message))}finally{w(!1)}},color:"primary",variant:"contained",disabled:j||"class"===b&&!v.trim()},"Search"))))};const te=Object(C.a)(e=>({paper:{padding:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},resultImage:{maxWidth:"100%",maxHeight:"400px",objectFit:"contain"},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},chipContainer:{display:"flex",gap:e.spacing(1),flexWrap:"wrap"}}));var ne=e=>{let{results:a}=e;const t=te();if(!a)return null;const{model:n,data:l}=a;if(l.error)return r.a.createElement(E.a,{sx:{p:2,bgcolor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},l.error));const o=()=>l.performance?r.a.createElement(s.a,{className:"performance-info"},r.a.createElement(L.a,{className:t.dividerMargin}),r.a.createElement(u.a,{variant:"body2"},"Inference time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(l.performance.inference_time)," on ",l.performance.device)):null;return"yolo"===n||"detr"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"yolo"===n?"YOLOv8":"DETR"," Detection Results"),r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12,md:6},l.image&&r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detection Result"),r.a.createElement("img",{src:"data:image/png;base64,".concat(l.image),alt:"Detection Result",className:t.resultImage}))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(s.a,{className:t.marginBottom},r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Detected Objects:"),l.detections&&l.detections.length>0?r.a.createElement(M.a,null,l.detections.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.class),r.a.createElement(B.a,{label:"".concat((100*e.confidence).toFixed(0),"%"),size:"small",color:"primary",style:{marginLeft:8}})),secondary:"Bounding Box: [".concat(e.bbox.join(", "),"]")})),a<l.detections.length-1&&r.a.createElement(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No objects detected")))),o(),r.a.createElement(ae,{results:a})):"vit"===n?r.a.createElement(E.a,{className:t.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"ViT Classification Results"),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"Top Predictions:"),l.top_predictions&&l.top_predictions.length>0?r.a.createElement(M.a,null,l.top_predictions.map((e,a)=>r.a.createElement(r.a.Fragment,{key:a},r.a.createElement(W.a,null,r.a.createElement(_.a,{primary:r.a.createElement(s.a,{style:{display:"flex",alignItems:"center"}},r.a.createElement(u.a,{variant:"body1",component:"span"},e.rank,". ",e.class),r.a.createElement(B.a,{label:"".concat((100*e.probability).toFixed(1),"%"),size:"small",color:0===a?"primary":"default",style:{marginLeft:8}}))})),a<l.top_predictions.length-1&&r.a.createElement(L.a,null)))):r.a.createElement(u.a,{variant:"body1"},"No classifications available"),o(),r.a.createElement(ae,{results:a})):null};const re=Object(C.a)(e=>({paper:{padding:e.spacing(2),marginTop:e.spacing(2)},marginBottom:{marginBottom:e.spacing(2)},dividerMargin:{margin:"".concat(e.spacing(2),"px 0")},responseBox:{padding:e.spacing(2),backgroundColor:"#f5f5f5",borderRadius:e.shape.borderRadius,marginTop:e.spacing(2),whiteSpace:"pre-wrap"},buttonProgress:{marginLeft:e.spacing(1)}}));var le=e=>{let{visionResults:a,model:t}=e;const l=re(),[o,c]=Object(n.useState)(""),[i,m]=Object(n.useState)(!1),[d,g]=Object(n.useState)(null),[p,b]=Object(n.useState)(null);return a?r.a.createElement(E.a,{className:l.paper},r.a.createElement(u.a,{variant:"h6",gutterBottom:!0},"Ask AI about the ","vit"===t?"Classification":"Detection"," Results"),r.a.createElement(u.a,{variant:"body2",className:l.marginBottom},"Ask a question about the detected objects or classifications to get an AI-powered analysis."),r.a.createElement(X.a,{fullWidth:!0,label:"Your question about the image",variant:"outlined",value:o,onChange:e=>c(e.target.value),disabled:i,className:l.marginBottom,placeholder:"vit"===t?"E.g., What category does this image belong to?":"E.g., How many people are in this image?"}),r.a.createElement(y.a,{variant:"contained",color:"primary",onClick:async()=>{if(o.trim()){m(!0),b(null);try{const e=await fetch("/api/analyze",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({visionResults:a,userQuery:o})});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const t=await e.json();t.error?b(t.error):g(t)}catch(e){console.error("Error analyzing with LLM:",e),b("Error analyzing with LLM: ".concat(e.message))}finally{m(!1)}}},disabled:i||!o.trim()},"Analyze with AI",i&&r.a.createElement(h.a,{size:24,className:l.buttonProgress})),p&&r.a.createElement(s.a,{mt:2},r.a.createElement(u.a,{color:"error"},p)),d&&r.a.createElement(r.a.Fragment,null,r.a.createElement(L.a,{className:l.dividerMargin}),r.a.createElement(u.a,{variant:"subtitle1",gutterBottom:!0},"AI Analysis:"),r.a.createElement(s.a,{className:l.responseBox},r.a.createElement(u.a,{variant:"body1"},d.response)),d.performance&&r.a.createElement(s.a,{mt:1},r.a.createElement(u.a,{variant:"body2",color:"textSecondary"},"Analysis time: ",(e=>{if(void 0===e||null===e||isNaN(e))return"-";const a=Number(e);return a<1e3?"".concat(a.toFixed(2)," ms"):"".concat((a/1e3).toFixed(2)," s")})(d.performance.inference_time)," on ",d.performance.device)))):null};var oe=function(){const[e,a]=Object(n.useState)("gpt-4"),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)("You are a helpful assistant."),[i,s]=Object(n.useState)(""),[m,d]=Object(n.useState)(""),[g,h]=Object(n.useState)(!1),[b,f]=Object(n.useState)("");return r.a.createElement(E.a,{style:{padding:16}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"OpenAI Chat (OpenAI API)"),r.a.createElement(u.a,{variant:"body2",color:"textSecondary",gutterBottom:!0},"If the server env var OPENAI_API_KEY is set, the API Key field is optional."),r.a.createElement(p.a,{container:!0,spacing:2},r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"Model",value:e,onChange:e=>a(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(X.a,{label:"OpenAI API Key (optional)",value:t,onChange:e=>l(e.target.value),fullWidth:!0,variant:"outlined",size:"small",type:"password",placeholder:"sk-..."})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"System Prompt (optional)",value:o,onChange:e=>c(e.target.value),fullWidth:!0,variant:"outlined",size:"small"})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(X.a,{label:"User Question",value:i,onChange:e=>s(e.target.value),fullWidth:!0,multiline:!0,rows:4,variant:"outlined"})),b&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(u.a,{color:"error"},b)),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement("div",{style:{display:"flex",gap:8}},r.a.createElement(y.a,{color:"primary",variant:"contained",onClick:async()=>{f(""),d("");const a=(i||"").trim();if(a){h(!0);try{const r=await fetch("/api/openai/chat",{method:"POST",headers:{"Content-Type":"application/json"},credentials:"include",body:JSON.stringify({prompt:a,model:(e||"").trim()||"gpt-4",api_key:t||void 0,system:o||void 0})});if(!r.ok){let e=await r.text();try{e=JSON.stringify(JSON.parse(e),null,2)}catch(n){}throw new Error(e)}const l=await r.json(),c="Model: ".concat(l.model," | Latency: ").concat(l.latency_sec,"s")+(l.usage?" | Usage: ".concat(JSON.stringify(l.usage)):"");d((l.response||"(Empty response)")+"\n\n---\n"+c)}catch(r){f("Error: "+r.message)}finally{h(!1)}}else f("Please enter a question.")},disabled:g},g?"Sending...":"Send Question"),r.a.createElement(y.a,{variant:"outlined",onClick:()=>{s(""),d(""),f("")}},"Clear"))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(L.a,{style:{margin:"12px 0"}}),r.a.createElement(u.a,{variant:"subtitle2",color:"textSecondary"},"Response"),r.a.createElement("pre",{style:{whiteSpace:"pre-wrap",fontFamily:"ui-monospace, monospace"}},m))))};t(102);const ce=Object(c.a)({palette:{primary:{main:"#3f51b5"},secondary:{main:"#f50057"}},typography:{fontFamily:"Roboto, Arial, sans-serif"}});var ie=function(){const[e,a]=Object(n.useState)(null),[t,l]=Object(n.useState)(""),[o,c]=Object(n.useState)(!1),[y,b]=Object(n.useState)(null),[f,v]=Object(n.useState)(null),[x,S]=Object(n.useState)({yolo:!1,detr:!1,vit:!1});return Object(n.useEffect)(()=>{fetch("/api/status").then(e=>e.json()).then(e=>{S(e.models)}).catch(e=>{console.error("Error checking API status:",e),v("Error connecting to the backend API. Please make sure the server is running.")})},[]),r.a.createElement(i.a,{theme:ce},r.a.createElement(s.a,{style:{flexGrow:1}},r.a.createElement(m.a,{position:"static"},r.a.createElement(d.a,null,r.a.createElement(u.a,{variant:"h6",style:{flexGrow:1}},"Multi-Model Object Detection Demo"))),r.a.createElement(g.a,{maxWidth:"lg",style:{marginTop:ce.spacing(4),marginBottom:ce.spacing(4)}},r.a.createElement(p.a,{container:!0,spacing:3},r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2)}},r.a.createElement(u.a,{variant:"h5",gutterBottom:!0},"Upload an image to see how each model performs!"),r.a.createElement(u.a,{variant:"body1",paragraph:!0},"This demo showcases three different object detection and image classification models:"),r.a.createElement(u.a,{variant:"body1",component:"div"},r.a.createElement("ul",null,r.a.createElement("li",null,r.a.createElement("strong",null,"YOLOv8"),": Fast and accurate object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"DETR"),": DEtection TRansformer for object detection"),r.a.createElement("li",null,r.a.createElement("strong",null,"ViT"),": Vision Transformer for image classification"))))),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(w,{onImageUpload:e=>{a(e),b(null),v(null)}})),r.a.createElement(p.a,{item:!0,xs:12,md:6},r.a.createElement(z,{onModelSelect:e=>{l(e),b(null),v(null)},onProcess:async()=>{if(!e||!t)return void v("Please select both an image and a model");c(!0),v(null);const a=new FormData;a.append("image",e);let n="";switch(t){case"yolo":n="/api/detect/yolo";break;case"detr":n="/api/detect/detr";break;case"vit":n="/api/classify/vit";break;default:return v("Invalid model selection"),void c(!1)}try{const e=await fetch(n,{method:"POST",body:a});if(!e.ok)throw new Error("HTTP error! Status: ".concat(e.status));const r=await e.json();b({model:t,data:r})}catch(r){console.error("Error processing image:",r),v("Error processing image: ".concat(r.message))}finally{c(!1)}},isProcessing:o,modelsStatus:x,selectedModel:t,imageSelected:!!e})),f&&r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(E.a,{style:{padding:ce.spacing(2),backgroundColor:"#ffebee"}},r.a.createElement(u.a,{color:"error"},f))),o&&r.a.createElement(p.a,{item:!0,xs:12,style:{textAlign:"center",margin:"".concat(ce.spacing(4),"px 0")}},r.a.createElement(h.a,null),r.a.createElement(u.a,{variant:"h6",style:{marginTop:ce.spacing(2)}},"Processing image...")),y&&r.a.createElement(r.a.Fragment,null,r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(ne,{results:y})),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(le,{visionResults:y.data,model:y.model}))),r.a.createElement(p.a,{item:!0,xs:12},r.a.createElement(oe,null))))))};var se=e=>{e&&e instanceof Function&&t.e(3).then(t.bind(null,170)).then(a=>{let{getCLS:t,getFID:n,getFCP:r,getLCP:l,getTTFB:o}=a;t(e),n(e),r(e),l(e),o(e)})};o.a.render(r.a.createElement(r.a.StrictMode,null,r.a.createElement(ie,null)),document.getElementById("root")),se()},92:function(e,a,t){e.exports=t(103)},97:function(e,a,t){}},[[92,1,2]]]);
2
- //# sourceMappingURL=main.fc0e2dc7.chunk.js.map
 
 
 
static_bak_20250820_085943/static/js/main.fc0e2dc7.chunk.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["components/ImageUploader.js","components/ModelSelector.js","components/VectorDBActions.js","components/ResultDisplay.js","components/LlmAnalysis.js","components/OpenAIChat.js","App.js","reportWebVitals.js","index.js"],"names":["useStyles","makeStyles","theme","paper","padding","spacing","display","flexDirection","alignItems","height","minHeight","transition","dragActive","border","backgroundColor","dragInactive","uploadBox","justifyContent","width","cursor","uploadIcon","fontSize","color","marginBottom","supportText","marginTop","previewBox","position","imageContainer","breakpoints","down","overflow","deleteButton","top","right","ImageUploader","_ref","onImageUpload","classes","previewUrl","setPreviewUrl","useState","setDragActive","fileInputRef","useRef","handleDrag","e","preventDefault","stopPropagation","type","handleFiles","file","startsWith","URL","createObjectURL","alert","React","createElement","Paper","className","concat","onDragEnter","onDragLeave","onDragOver","onDrop","dataTransfer","files","ref","accept","onChange","target","style","Box","Typography","variant","gutterBottom","src","alt","maxHeight","objectFit","IconButton","aria-label","onClick","handleRemoveImage","current","value","DeleteIcon","onButtonClick","click","CloudUploadIcon","Button","component","startIcon","card","selectedCard","unavailableCard","opacity","cardContent","flexGrow","chipContainer","successChip","errorChip","modelType","processButton","textAlign","ModelSelector","onModelSelect","onProcess","isProcessing","modelsStatus","selectedModel","imageSelected","models","id","name","description","icon","VisibilityIcon","available","yolo","detr","CategoryIcon","vit","handleModelClick","modelId","find","m","sx","p","Grid","container","map","model","item","xs","sm","key","Card","CardContent","mb","Chip","label","size","CardActions","disabled","fullWidth","PlayArrowIcon","root","borderRadius","shape","button","marginRight","searchDialog","minWidth","formControl","searchResults","resultCard","resultImage","chip","margin","similarityChip","palette","primary","main","VectorDBActions","results","isSaving","setIsSaving","saveSuccess","setSaveSuccess","saveError","setSaveError","openSearchDialog","setOpenSearchDialog","searchType","setSearchType","searchClass","setSearchClass","setSearchResults","isSearching","setIsSearching","searchError","setSearchError","data","handleCloseSearchDialog","generateUUID","replace","c","r","Math","random","toString","async","response","fetch","method","headers","body","JSON","stringify","image","metadata","classifications","objects","detections","imageId","ok","Error","status","result","json","error","setTimeout","err","console","message","Fragment","CircularProgress","handleOpenSearchDialog","Alert","severity","Snackbar","open","autoHideDuration","onClose","Dialog","maxWidth","DialogTitle","DialogContent","FormControl","InputLabel","Select","labelId","event","MenuItem","TextField","placeholder","marginLeft","log","length","index","similarity","distance","image_data","CardMedia","class","toFixed","confidence","DialogActions","requestBody","n_results","trim","class_name","success","Array","isArray","dividerMargin","gap","flexWrap","ResultDisplay","bgcolor","renderPerformanceInfo","performance","Divider","ms","undefined","isNaN","num","Number","formatTime","inference_time","device","md","List","detection","ListItem","ListItemText","secondary","bbox","join","top_predictions","prediction","rank","probability","responseBox","whiteSpace","buttonProgress","LlmAnalysis","visionResults","userQuery","setUserQuery","isAnalyzing","setIsAnalyzing","analysisResult","setAnalysisResult","setError","mt","OpenAIChat","setModel","apiKey","setApiKey","system","setSystem","prompt","setPrompt","setResponse","loading","setLoading","multiline","rows","res","credentials","api_key","txt","text","parse","_unused","meta","latency_sec","usage","onClear","fontFamily","createMuiTheme","typography","App","selectedImage","setSelectedImage","setSelectedModel","setIsProcessing","setResults","setModelsStatus","useEffect","then","catch","ThemeProvider","AppBar","Toolbar","Container","paragraph","formData","FormData","append","endpoint","reportWebVitals","onPerfEntry","Function","getCLS","getFID","getFCP","getLCP","getTTFB","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"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","file":"static/js/main.fc0e2dc7.chunk.js","sourcesContent":["import React, { useState, useRef } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n Button, \n IconButton \n} from '@material-ui/core';\nimport CloudUploadIcon from '@material-ui/icons/CloudUpload';\nimport DeleteIcon from '@material-ui/icons/Delete';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n height: '100%',\n minHeight: 300,\n transition: 'all 0.3s ease'\n },\n dragActive: {\n border: '2px dashed #3f51b5',\n backgroundColor: 'rgba(63, 81, 181, 0.05)'\n },\n dragInactive: {\n border: '2px dashed #ccc',\n backgroundColor: 'white'\n },\n uploadBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n width: '100%',\n cursor: 'pointer'\n },\n uploadIcon: {\n fontSize: 60,\n color: '#3f51b5',\n marginBottom: theme.spacing(2)\n },\n supportText: {\n marginTop: theme.spacing(2)\n },\n previewBox: {\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n width: '100%',\n height: '100%',\n position: 'relative'\n },\n imageContainer: {\n position: 'relative',\n width: '100%',\n // Use viewport-based height so any aspect ratio fits inside\n height: '60vh',\n [theme.breakpoints.down('sm')]: {\n height: '45vh',\n },\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n overflow: 'hidden',\n marginTop: theme.spacing(2),\n },\n deleteButton: {\n position: 'absolute',\n top: 0,\n right: 0,\n backgroundColor: 'rgba(255, 255, 255, 0.7)',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.9)',\n }\n }\n}));\n\nconst ImageUploader = ({ onImageUpload }) => {\n const classes = useStyles();\n const [previewUrl, setPreviewUrl] = useState(null);\n const [dragActive, setDragActive] = useState(false);\n const fileInputRef = useRef(null);\n\n const handleDrag = (e) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.type === 'dragenter' || e.type === 'dragover') {\n setDragActive(true);\n } else if (e.type === 'dragleave') {\n setDragActive(false);\n }\n };\n\n const handleDrop = (e) => {\n e.preventDefault();\n e.stopPropagation();\n setDragActive(false);\n if (e.dataTransfer.files && e.dataTransfer.files[0]) {\n handleFiles(e.dataTransfer.files[0]);\n }\n };\n\n const handleChange = (e) => {\n e.preventDefault();\n if (e.target.files && e.target.files[0]) {\n handleFiles(e.target.files[0]);\n }\n };\n\n const handleFiles = (file) => {\n if (file.type.startsWith('image/')) {\n setPreviewUrl(URL.createObjectURL(file));\n onImageUpload(file);\n } else {\n alert('Please upload an image file');\n }\n };\n\n const onButtonClick = () => {\n fileInputRef.current.click();\n };\n\n const handleRemoveImage = () => {\n setPreviewUrl(null);\n onImageUpload(null);\n fileInputRef.current.value = \"\";\n };\n\n return (\n <Paper \n className={`${classes.paper} ${dragActive ? classes.dragActive : classes.dragInactive}`}\n onDragEnter={handleDrag}\n onDragLeave={handleDrag}\n onDragOver={handleDrag}\n onDrop={handleDrop}\n >\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleChange}\n style={{ display: 'none' }}\n />\n\n {!previewUrl ? (\n <Box \n className={classes.uploadBox}\n onClick={onButtonClick}\n >\n <CloudUploadIcon className={classes.uploadIcon} />\n <Typography variant=\"h6\" gutterBottom>\n Drag & Drop an image here\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n or\n </Typography>\n <Button\n variant=\"contained\"\n color=\"primary\"\n component=\"span\"\n startIcon={<CloudUploadIcon />}\n >\n Browse Files\n </Button>\n <Typography variant=\"body2\" color=\"textSecondary\" className={classes.supportText}>\n Supported formats: JPG, PNG, GIF\n </Typography>\n </Box>\n ) : (\n <Box className={classes.previewBox}>\n <Typography variant=\"h6\" gutterBottom>\n Preview\n </Typography>\n <Box className={classes.imageContainer}>\n <img\n src={previewUrl}\n alt=\"Preview\"\n className=\"preview-image\"\n style={{\n width: '100%',\n height: 'auto',\n maxHeight: '100%',\n objectFit: 'contain',\n display: 'block'\n }}\n />\n <IconButton\n aria-label=\"delete\"\n className={classes.deleteButton}\n onClick={handleRemoveImage}\n >\n <DeleteIcon />\n </IconButton>\n </Box>\n </Box>\n )}\n </Paper>\n );\n};\n\nexport default ImageUploader;\n","import React from 'react';\nimport { \n Grid, \n Card, \n CardContent, \n CardActions, \n Typography, \n Button, \n Chip,\n Box\n} from '@material-ui/core';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport CategoryIcon from '@material-ui/icons/Category';\nimport PlayArrowIcon from '@material-ui/icons/PlayArrow';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n card: {\n height: '100%',\n display: 'flex',\n flexDirection: 'column',\n },\n selectedCard: {\n border: '2px solid #3f51b5',\n },\n unavailableCard: {\n opacity: 0.6,\n },\n cardContent: {\n flexGrow: 1,\n },\n chipContainer: {\n marginBottom: theme.spacing(1.5),\n },\n successChip: {\n backgroundColor: '#34C759',\n color: '#fff',\n },\n errorChip: {\n backgroundColor: '#FF3B3F',\n color: '#fff',\n },\n modelType: {\n marginTop: theme.spacing(1),\n },\n processButton: {\n marginTop: theme.spacing(3),\n textAlign: 'center',\n }\n}));\n\nconst ModelSelector = ({ \n onModelSelect, \n onProcess, \n isProcessing, \n modelsStatus, \n selectedModel,\n imageSelected \n}) => {\n const classes = useStyles();\n \n const models = [\n {\n id: 'yolo',\n name: 'YOLOv8',\n description: 'Fast and accurate object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.yolo\n },\n {\n id: 'detr',\n name: 'DETR',\n description: 'DEtection TRansformer for object detection',\n icon: <VisibilityIcon />,\n available: modelsStatus.detr\n },\n {\n id: 'vit',\n name: 'ViT',\n description: 'Vision Transformer for image classification',\n icon: <CategoryIcon />,\n available: modelsStatus.vit\n }\n ];\n\n const handleModelClick = (modelId) => {\n if (models.find(m => m.id === modelId).available) {\n onModelSelect(modelId);\n }\n };\n\n return (\n <Box sx={{ p: 2, height: '100%' }}>\n <Typography variant=\"h6\" gutterBottom>\n Select Model\n </Typography>\n \n <Grid container spacing={2}>\n {models.map((model) => (\n <Grid item xs={12} sm={4} key={model.id}>\n <Card \n className={`\n ${classes.card} \n ${selectedModel === model.id ? classes.selectedCard : ''} \n ${!model.available ? classes.unavailableCard : ''}\n `}\n onClick={() => handleModelClick(model.id)}\n >\n <CardContent className={classes.cardContent}>\n <Box sx={{ mb: 2, color: 'primary' }}>\n {model.icon}\n </Box>\n <Typography variant=\"h5\" component=\"div\" gutterBottom>\n {model.name}\n </Typography>\n <div className={classes.chipContainer}>\n {model.available ? (\n <Chip \n label=\"Available\" \n className={classes.successChip}\n size=\"small\" \n />\n ) : (\n <Chip \n label=\"Not Available\" \n className={classes.errorChip}\n size=\"small\" \n />\n )}\n </div>\n <Typography variant=\"body2\" color=\"textSecondary\">\n {model.description}\n </Typography>\n </CardContent>\n <CardActions>\n <Button \n size=\"small\" \n onClick={() => handleModelClick(model.id)}\n disabled={!model.available}\n color={selectedModel === model.id ? \"primary\" : \"default\"}\n variant={selectedModel === model.id ? \"contained\" : \"outlined\"}\n fullWidth\n >\n {selectedModel === model.id ? 'Selected' : 'Select'}\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n\n <div className={classes.processButton}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"large\"\n startIcon={<PlayArrowIcon />}\n onClick={onProcess}\n disabled={!selectedModel || !imageSelected || isProcessing}\n >\n {isProcessing ? 'Processing...' : 'Process Image'}\n </Button>\n </div>\n </Box>\n );\n};\n\nexport default ModelSelector;\n","import React, { useState } from 'react';\nimport { \n Button, \n Box, \n Typography, \n CircularProgress, \n Snackbar,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n TextField,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n Grid,\n Card,\n CardMedia,\n CardContent,\n Chip\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n root: {\n marginTop: theme.spacing(2),\n marginBottom: theme.spacing(2),\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n },\n button: {\n marginRight: theme.spacing(2),\n },\n searchDialog: {\n minWidth: '500px',\n },\n formControl: {\n marginBottom: theme.spacing(2),\n minWidth: '100%',\n },\n searchResults: {\n marginTop: theme.spacing(2),\n },\n resultCard: {\n marginBottom: theme.spacing(2),\n },\n resultImage: {\n height: 140,\n objectFit: 'contain',\n },\n chip: {\n margin: theme.spacing(0.5),\n },\n similarityChip: {\n backgroundColor: theme.palette.primary.main,\n color: 'white',\n }\n}));\n\nconst VectorDBActions = ({ results }) => {\n const classes = useStyles();\n const [isSaving, setIsSaving] = useState(false);\n const [saveSuccess, setSaveSuccess] = useState(false);\n const [saveError, setSaveError] = useState(null);\n const [openSearchDialog, setOpenSearchDialog] = useState(false);\n const [searchType, setSearchType] = useState('image');\n const [searchClass, setSearchClass] = useState('');\n const [searchResults, setSearchResults] = useState([]);\n const [isSearching, setIsSearching] = useState(false);\n const [searchError, setSearchError] = useState(null);\n \n // Extract model and data from results\n const { model, data } = results;\n \n // Handle saving to vector DB\n const handleSaveToVectorDB = async () => {\n setIsSaving(true);\n setSaveError(null);\n \n try {\n let response;\n \n if (model === 'vit') {\n // For ViT, save the whole image with classifications\n response = await fetch('/api/add-to-collection', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n metadata: {\n model: 'vit',\n classifications: data.classifications\n }\n })\n });\n } else {\n // For YOLO and DETR, save detected objects\n response = await fetch('/api/add-detected-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n image: data.image,\n objects: data.detections,\n imageId: generateUUID()\n })\n });\n }\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n setSaveSuccess(true);\n setTimeout(() => setSaveSuccess(false), 5000);\n } catch (err) {\n console.error('Error saving to vector DB:', err);\n setSaveError(`Error saving to vector DB: ${err.message}`);\n } finally {\n setIsSaving(false);\n }\n };\n \n // Handle opening search dialog\n const handleOpenSearchDialog = () => {\n setOpenSearchDialog(true);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle closing search dialog\n const handleCloseSearchDialog = () => {\n setOpenSearchDialog(false);\n };\n \n // Handle search type change\n const handleSearchTypeChange = (event) => {\n setSearchType(event.target.value);\n setSearchResults([]);\n setSearchError(null);\n };\n \n // Handle search class change\n const handleSearchClassChange = (event) => {\n setSearchClass(event.target.value);\n };\n \n // Handle search\n const handleSearch = async () => {\n setIsSearching(true);\n setSearchError(null);\n \n try {\n let requestBody = {};\n \n if (searchType === 'image') {\n // Search by current image\n requestBody = {\n searchType: 'image',\n image: data.image,\n n_results: 5\n };\n } else {\n // Search by class name\n if (!searchClass.trim()) {\n throw new Error('Please enter a class name');\n }\n \n requestBody = {\n searchType: 'class',\n class_name: searchClass.trim(),\n n_results: 5\n };\n }\n \n const response = await fetch('/api/search-similar-objects', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(requestBody)\n });\n \n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n \n const result = await response.json();\n \n if (result.error) {\n throw new Error(result.error);\n }\n \n console.log('Search API response:', result);\n \n // The backend responds with {success, searchType, results} structure, so extract only the results array\n if (result.success && Array.isArray(result.results)) {\n console.log('Setting search results array:', result.results);\n console.log('Results array length:', result.results.length);\n console.log('First result item:', result.results[0]);\n setSearchResults(result.results);\n } else {\n console.error('Unexpected API response format:', result);\n throw new Error('Unexpected API response format');\n }\n } catch (err) {\n console.error('Error searching vector DB:', err);\n setSearchError(`Error searching vector DB: ${err.message}`);\n } finally {\n setIsSearching(false);\n }\n };\n \n // Generate UUID for image ID\n const generateUUID = () => {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n const r = Math.random() * 16 | 0;\n const v = c === 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n };\n \n // Render search results\n const renderSearchResults = () => {\n console.log('Rendering search results:', searchResults);\n console.log('Search results length:', searchResults.length);\n \n if (searchResults.length === 0) {\n console.log('No results to render');\n return (\n <Typography variant=\"body1\">No results found.</Typography>\n );\n }\n \n return (\n <Grid container spacing={2}>\n {searchResults.map((result, index) => {\n const similarity = (1 - result.distance) * 100;\n \n return (\n <Grid item xs={12} sm={6} key={index}>\n <Card className={classes.resultCard}>\n {result.metadata && result.metadata.image_data ? (\n <CardMedia\n className={classes.resultImage}\n component=\"img\"\n height=\"200\"\n image={`data:image/jpeg;base64,${result.metadata.image_data}`}\n alt={result.metadata && result.metadata.class ? result.metadata.class : 'Object'}\n />\n ) : (\n <Box \n className={classes.resultImage}\n style={{ \n backgroundColor: '#f0f0f0', \n display: 'flex', \n alignItems: 'center', \n justifyContent: 'center',\n height: 200\n }}\n >\n <Typography variant=\"body2\" color=\"textSecondary\">\n {result.metadata && result.metadata.class ? result.metadata.class : 'Object'} Image\n </Typography>\n </Box>\n )}\n <CardContent>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"subtitle1\">Result #{index + 1}</Typography>\n <Chip \n label={`Similarity: ${similarity.toFixed(2)}%`}\n className={classes.similarityChip}\n size=\"small\"\n />\n </Box>\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Class:</strong> {result.metadata.class || 'N/A'}\n </Typography>\n {result.metadata.confidence && (\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Confidence:</strong> {(result.metadata.confidence * 100).toFixed(2)}%\n </Typography>\n )}\n <Typography variant=\"body2\" color=\"textSecondary\">\n <strong>Object ID:</strong> {result.id}\n </Typography>\n </CardContent>\n </Card>\n </Grid>\n );\n })}\n </Grid>\n );\n };\n \n return (\n <Box className={classes.root}>\n <Typography variant=\"h6\" gutterBottom>\n Vector Database Actions\n </Typography>\n \n <Box display=\"flex\" alignItems=\"center\" mb={2}>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleSaveToVectorDB}\n disabled={isSaving}\n className={classes.button}\n >\n {isSaving ? (\n <>\n <CircularProgress size={20} color=\"inherit\" style={{ marginRight: 8 }} />\n Saving...\n </>\n ) : (\n 'Save to Vector DB'\n )}\n </Button>\n \n <Button\n variant=\"outlined\"\n color=\"primary\"\n onClick={handleOpenSearchDialog}\n className={classes.button}\n >\n Search Similar\n </Button>\n </Box>\n \n {saveError && (\n <Alert severity=\"error\" style={{ marginTop: 8 }}>\n {saveError}\n </Alert>\n )}\n \n <Snackbar open={saveSuccess} autoHideDuration={5000} onClose={() => setSaveSuccess(false)}>\n <Alert severity=\"success\">\n {model === 'vit' ? (\n 'Image and classifications successfully saved to vector DB!'\n ) : (\n 'Detected objects successfully saved to vector DB!'\n )}\n </Alert>\n </Snackbar>\n \n {/* Search Dialog */}\n <Dialog\n open={openSearchDialog}\n onClose={handleCloseSearchDialog}\n maxWidth=\"md\"\n fullWidth\n >\n <DialogTitle>Search Vector Database</DialogTitle>\n <DialogContent>\n <FormControl className={classes.formControl}>\n <InputLabel id=\"search-type-label\">Search Type</InputLabel>\n <Select\n labelId=\"search-type-label\"\n id=\"search-type\"\n value={searchType}\n onChange={handleSearchTypeChange}\n >\n <MenuItem value=\"image\">Search by Current Image</MenuItem>\n <MenuItem value=\"class\">Search by Class Name</MenuItem>\n </Select>\n </FormControl>\n \n {searchType === 'class' && (\n <FormControl className={classes.formControl}>\n <TextField\n label=\"Class Name\"\n value={searchClass}\n onChange={handleSearchClassChange}\n placeholder=\"e.g. person, car, dog...\"\n fullWidth\n />\n </FormControl>\n )}\n \n {searchError && (\n <Alert severity=\"error\" style={{ marginBottom: 16 }}>\n {searchError}\n </Alert>\n )}\n \n <Box className={classes.searchResults}>\n {isSearching ? (\n <Box display=\"flex\" justifyContent=\"center\" alignItems=\"center\" p={4}>\n <CircularProgress />\n <Typography variant=\"body1\" style={{ marginLeft: 16 }}>\n Searching...\n </Typography>\n </Box>\n ) : (\n <>\n {console.log('Search dialog render - searchResults:', searchResults)}\n {searchResults.length > 0 ? renderSearchResults() : \n <Typography variant=\"body1\">No results found. Please try another search.</Typography>\n }\n </>\n )}\n </Box>\n </DialogContent>\n <DialogActions>\n <Button onClick={handleCloseSearchDialog} color=\"default\">\n Close\n </Button>\n <Button \n onClick={handleSearch} \n color=\"primary\" \n variant=\"contained\"\n disabled={isSearching || (searchType === 'class' && !searchClass.trim())}\n >\n Search\n </Button>\n </DialogActions>\n </Dialog>\n </Box>\n );\n};\n\nexport default VectorDBActions;\n","import React from 'react';\nimport { \n Paper, \n Typography, \n Box, \n List, \n ListItem, \n ListItemText, \n Divider,\n Grid,\n Chip\n} from '@material-ui/core';\nimport VectorDBActions from './VectorDBActions';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n resultImage: {\n maxWidth: '100%',\n maxHeight: '400px',\n objectFit: 'contain'\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n chipContainer: {\n display: 'flex',\n gap: theme.spacing(1),\n flexWrap: 'wrap'\n }\n}));\n\nconst ResultDisplay = ({ results }) => {\n const classes = useStyles();\n if (!results) return null;\n \n const { model, data } = results;\n \n // Helper to format times nicely\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n \n // Check if there's an error\n if (data.error) {\n return (\n <Paper sx={{ p: 2, bgcolor: '#ffebee' }}>\n <Typography color=\"error\">{data.error}</Typography>\n </Paper>\n );\n }\n\n // Display performance info\n const renderPerformanceInfo = () => {\n if (!data.performance) return null;\n \n return (\n <Box className=\"performance-info\">\n <Divider className={classes.dividerMargin} />\n <Typography variant=\"body2\">\n Inference time: {formatTime(data.performance.inference_time)} on {data.performance.device}\n </Typography>\n </Box>\n );\n };\n\n // Render for YOLO and DETR (object detection)\n if (model === 'yolo' || model === 'detr') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n {model === 'yolo' ? 'YOLOv8' : 'DETR'} Detection Results\n </Typography>\n \n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n {data.image && (\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detection Result\n </Typography>\n <img \n src={`data:image/png;base64,${data.image}`} \n alt=\"Detection Result\" \n className={classes.resultImage}\n />\n </Box>\n )}\n </Grid>\n \n <Grid item xs={12} md={6}>\n <Box className={classes.marginBottom}>\n <Typography variant=\"subtitle1\" gutterBottom>\n Detected Objects:\n </Typography>\n \n {data.detections && data.detections.length > 0 ? (\n <List>\n {data.detections.map((detection, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {detection.class}\n </Typography>\n <Chip \n label={`${(detection.confidence * 100).toFixed(0)}%`}\n size=\"small\"\n color=\"primary\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n secondary={`Bounding Box: [${detection.bbox.join(', ')}]`} \n />\n </ListItem>\n {index < data.detections.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No objects detected</Typography>\n )}\n </Box>\n </Grid>\n </Grid>\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for Object Detection */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n // Render for ViT (classification)\n if (model === 'vit') {\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n ViT Classification Results\n </Typography>\n \n <Typography variant=\"subtitle1\" gutterBottom>\n Top Predictions:\n </Typography>\n \n {data.top_predictions && data.top_predictions.length > 0 ? (\n <List>\n {data.top_predictions.map((prediction, index) => (\n <React.Fragment key={index}>\n <ListItem>\n <ListItemText \n primary={\n <Box style={{ display: 'flex', alignItems: 'center' }}>\n <Typography variant=\"body1\" component=\"span\">\n {prediction.rank}. {prediction.class}\n </Typography>\n <Chip \n label={`${(prediction.probability * 100).toFixed(1)}%`}\n size=\"small\"\n color={index === 0 ? \"primary\" : \"default\"}\n style={{ marginLeft: 8 }}\n />\n </Box>\n } \n />\n </ListItem>\n {index < data.top_predictions.length - 1 && <Divider />}\n </React.Fragment>\n ))}\n </List>\n ) : (\n <Typography variant=\"body1\">No classifications available</Typography>\n )}\n \n {renderPerformanceInfo()}\n \n {/* Vector DB Actions for ViT Classification */}\n <VectorDBActions results={results} />\n </Paper>\n );\n }\n \n return null;\n};\n\nexport default ResultDisplay;\n","import React, { useState } from 'react';\nimport { \n Paper, \n Typography, \n Box, \n TextField, \n Button, \n CircularProgress,\n Divider\n} from '@material-ui/core';\nimport { makeStyles } from '@material-ui/core/styles';\n\nconst useStyles = makeStyles((theme) => ({\n paper: {\n padding: theme.spacing(2),\n marginTop: theme.spacing(2)\n },\n marginBottom: {\n marginBottom: theme.spacing(2)\n },\n dividerMargin: {\n margin: `${theme.spacing(2)}px 0`\n },\n responseBox: {\n padding: theme.spacing(2),\n backgroundColor: '#f5f5f5',\n borderRadius: theme.shape.borderRadius,\n marginTop: theme.spacing(2),\n whiteSpace: 'pre-wrap'\n },\n buttonProgress: {\n marginLeft: theme.spacing(1)\n }\n}));\n\nconst LlmAnalysis = ({ visionResults, model }) => {\n const classes = useStyles();\n const [userQuery, setUserQuery] = useState('');\n const [isAnalyzing, setIsAnalyzing] = useState(false);\n const [analysisResult, setAnalysisResult] = useState(null);\n const [error, setError] = useState(null);\n\n // Format time for display\n const formatTime = (ms) => {\n if (ms === undefined || ms === null || isNaN(ms)) return '-';\n const num = Number(ms);\n if (num < 1000) return `${num.toFixed(2)} ms`;\n return `${(num / 1000).toFixed(2)} s`;\n };\n\n const handleAnalyze = async () => {\n if (!userQuery.trim()) return;\n \n setIsAnalyzing(true);\n setError(null);\n \n try {\n const response = await fetch('/api/analyze', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n visionResults: visionResults,\n userQuery: userQuery\n }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n \n if (data.error) {\n setError(data.error);\n } else {\n setAnalysisResult(data);\n }\n } catch (err) {\n console.error('Error analyzing with LLM:', err);\n setError(`Error analyzing with LLM: ${err.message}`);\n } finally {\n setIsAnalyzing(false);\n }\n };\n\n if (!visionResults) return null;\n\n return (\n <Paper className={classes.paper}>\n <Typography variant=\"h6\" gutterBottom>\n Ask AI about the {model === 'vit' ? 'Classification' : 'Detection'} Results\n </Typography>\n \n <Typography variant=\"body2\" className={classes.marginBottom}>\n Ask a question about the detected objects or classifications to get an AI-powered analysis.\n </Typography>\n \n <TextField\n fullWidth\n label=\"Your question about the image\"\n variant=\"outlined\"\n value={userQuery}\n onChange={(e) => setUserQuery(e.target.value)}\n disabled={isAnalyzing}\n className={classes.marginBottom}\n placeholder={model === 'vit' \n ? \"E.g., What category does this image belong to?\" \n : \"E.g., How many people are in this image?\"}\n />\n \n <Button \n variant=\"contained\" \n color=\"primary\"\n onClick={handleAnalyze}\n disabled={isAnalyzing || !userQuery.trim()}\n >\n Analyze with AI\n {isAnalyzing && <CircularProgress size={24} className={classes.buttonProgress} />}\n </Button>\n \n {error && (\n <Box mt={2}>\n <Typography color=\"error\">{error}</Typography>\n </Box>\n )}\n \n {analysisResult && (\n <>\n <Divider className={classes.dividerMargin} />\n \n <Typography variant=\"subtitle1\" gutterBottom>\n AI Analysis:\n </Typography>\n \n <Box className={classes.responseBox}>\n <Typography variant=\"body1\">\n {analysisResult.response}\n </Typography>\n </Box>\n \n {analysisResult.performance && (\n <Box mt={1}>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Analysis time: {formatTime(analysisResult.performance.inference_time)} on {analysisResult.performance.device}\n </Typography>\n </Box>\n )}\n </>\n )}\n </Paper>\n );\n};\n\nexport default LlmAnalysis;\n","import React, { useState } from 'react';\nimport { \n Paper,\n Typography,\n Grid,\n TextField,\n Button,\n Divider\n} from '@material-ui/core';\n\nfunction OpenAIChat() {\n const [model, setModel] = useState('gpt-4');\n const [apiKey, setApiKey] = useState('');\n const [system, setSystem] = useState('You are a helpful assistant.');\n const [prompt, setPrompt] = useState('');\n const [response, setResponse] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState('');\n\n const onSend = async () => {\n setError('');\n setResponse('');\n const p = (prompt || '').trim();\n if (!p) { setError('Please enter a question.'); return; }\n\n setLoading(true);\n try {\n const res = await fetch('/api/openai/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n credentials: 'include',\n body: JSON.stringify({\n prompt: p,\n model: (model || '').trim() || 'gpt-4',\n api_key: (apiKey || undefined),\n system: (system || undefined)\n })\n });\n\n if (!res.ok) {\n let txt = await res.text();\n try { txt = JSON.stringify(JSON.parse(txt), null, 2); } catch {}\n throw new Error(txt);\n }\n const data = await res.json();\n const meta = `Model: ${data.model} | Latency: ${data.latency_sec}s` + (data.usage ? ` | Usage: ${JSON.stringify(data.usage)}` : '');\n setResponse((data.response || '(Empty response)') + '\\n\\n---\\n' + meta);\n } catch (e) {\n setError('Error: ' + e.message);\n } finally {\n setLoading(false);\n }\n };\n\n const onClear = () => {\n setPrompt('');\n setResponse('');\n setError('');\n };\n\n return (\n <Paper style={{ padding: 16 }}>\n <Typography variant=\"h5\" gutterBottom>\n OpenAI Chat (OpenAI API)\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n If the server env var OPENAI_API_KEY is set, the API Key field is optional.\n </Typography>\n <Grid container spacing={2}>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"Model\"\n value={model}\n onChange={(e) => setModel(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12} md={6}>\n <TextField\n label=\"OpenAI API Key (optional)\"\n value={apiKey}\n onChange={(e) => setApiKey(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n type=\"password\"\n placeholder=\"sk-...\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"System Prompt (optional)\"\n value={system}\n onChange={(e) => setSystem(e.target.value)}\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n />\n </Grid>\n <Grid item xs={12}>\n <TextField\n label=\"User Question\"\n value={prompt}\n onChange={(e) => setPrompt(e.target.value)}\n fullWidth\n multiline\n rows={4}\n variant=\"outlined\"\n />\n </Grid>\n {error && (\n <Grid item xs={12}>\n <Typography color=\"error\">{error}</Typography>\n </Grid>\n )}\n <Grid item xs={12}>\n <div style={{ display: 'flex', gap: 8 }}>\n <Button color=\"primary\" variant=\"contained\" onClick={onSend} disabled={loading}>\n {loading ? 'Sending...' : 'Send Question'}\n </Button>\n <Button variant=\"outlined\" onClick={onClear}>Clear</Button>\n </div>\n </Grid>\n <Grid item xs={12}>\n <Divider style={{ margin: '12px 0' }} />\n <Typography variant=\"subtitle2\" color=\"textSecondary\">Response</Typography>\n <pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'ui-monospace, monospace' }}>{response}</pre>\n </Grid>\n </Grid>\n </Paper>\n );\n}\n\nexport default OpenAIChat;\n","import React, { useState, useEffect } from 'react';\nimport { \n Container, \n Typography, \n Box, \n Paper, \n Grid, \n CircularProgress,\n AppBar,\n Toolbar,\n ThemeProvider,\n createMuiTheme\n} from '@material-ui/core';\nimport ImageUploader from './components/ImageUploader';\nimport ModelSelector from './components/ModelSelector';\nimport ResultDisplay from './components/ResultDisplay';\nimport LlmAnalysis from './components/LlmAnalysis';\nimport OpenAIChat from './components/OpenAIChat';\nimport './App.css';\n\n// Create a theme\nconst theme = createMuiTheme({\n palette: {\n primary: {\n main: '#3f51b5',\n },\n secondary: {\n main: '#f50057',\n },\n },\n typography: {\n fontFamily: 'Roboto, Arial, sans-serif',\n },\n});\n\nfunction App() {\n const [selectedImage, setSelectedImage] = useState(null);\n const [selectedModel, setSelectedModel] = useState('');\n const [isProcessing, setIsProcessing] = useState(false);\n const [results, setResults] = useState(null);\n const [error, setError] = useState(null);\n const [modelsStatus, setModelsStatus] = useState({\n yolo: false,\n detr: false,\n vit: false\n });\n\n // Check API status on component mount\n useEffect(() => {\n fetch('/api/status')\n .then(response => response.json())\n .then(data => {\n setModelsStatus(data.models);\n })\n .catch(err => {\n console.error('Error checking API status:', err);\n setError('Error connecting to the backend API. Please make sure the server is running.');\n });\n }, []);\n\n const handleImageUpload = (image) => {\n setSelectedImage(image);\n setResults(null);\n setError(null);\n };\n\n const handleModelSelect = (model) => {\n setSelectedModel(model);\n setResults(null);\n setError(null);\n };\n\n const processImage = async () => {\n if (!selectedImage || !selectedModel) {\n setError('Please select both an image and a model');\n return;\n }\n\n setIsProcessing(true);\n setError(null);\n\n // Create form data for the image\n const formData = new FormData();\n formData.append('image', selectedImage);\n\n let endpoint = '';\n switch (selectedModel) {\n case 'yolo':\n endpoint = '/api/detect/yolo';\n break;\n case 'detr':\n endpoint = '/api/detect/detr';\n break;\n case 'vit':\n endpoint = '/api/classify/vit';\n break;\n default:\n setError('Invalid model selection');\n setIsProcessing(false);\n return;\n }\n\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! Status: ${response.status}`);\n }\n\n const data = await response.json();\n setResults({ model: selectedModel, data });\n } catch (err) {\n console.error('Error processing image:', err);\n setError(`Error processing image: ${err.message}`);\n } finally {\n setIsProcessing(false);\n }\n };\n\n return (\n <ThemeProvider theme={theme}>\n <Box style={{ flexGrow: 1 }}>\n <AppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" style={{ flexGrow: 1 }}>\n Multi-Model Object Detection Demo\n </Typography>\n </Toolbar>\n </AppBar>\n <Container maxWidth=\"lg\" style={{ marginTop: theme.spacing(4), marginBottom: theme.spacing(4) }}>\n <Grid container spacing={3}>\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2) }}>\n <Typography variant=\"h5\" gutterBottom>\n Upload an image to see how each model performs!\n </Typography>\n <Typography variant=\"body1\" paragraph>\n This demo showcases three different object detection and image classification models:\n </Typography>\n <Typography variant=\"body1\" component=\"div\">\n <ul>\n <li><strong>YOLOv8</strong>: Fast and accurate object detection</li>\n <li><strong>DETR</strong>: DEtection TRansformer for object detection</li>\n <li><strong>ViT</strong>: Vision Transformer for image classification</li>\n </ul>\n </Typography>\n </Paper>\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ImageUploader onImageUpload={handleImageUpload} />\n </Grid>\n \n <Grid item xs={12} md={6}>\n <ModelSelector \n onModelSelect={handleModelSelect} \n onProcess={processImage}\n isProcessing={isProcessing}\n modelsStatus={modelsStatus}\n selectedModel={selectedModel}\n imageSelected={!!selectedImage}\n />\n </Grid>\n \n {error && (\n <Grid item xs={12}>\n <Paper style={{ padding: theme.spacing(2), backgroundColor: '#ffebee' }}>\n <Typography color=\"error\">{error}</Typography>\n </Paper>\n </Grid>\n )}\n \n {isProcessing && (\n <Grid item xs={12} style={{ textAlign: 'center', margin: `${theme.spacing(4)}px 0` }}>\n <CircularProgress />\n <Typography variant=\"h6\" style={{ marginTop: theme.spacing(2) }}>\n Processing image...\n </Typography>\n </Grid>\n )}\n \n {results && (\n <>\n <Grid item xs={12}>\n <ResultDisplay results={results} />\n </Grid>\n <Grid item xs={12}>\n <LlmAnalysis visionResults={results.data} model={results.model} />\n </Grid>\n </>\n )}\n\n {/* OpenAI Chat section at the end */}\n <Grid item xs={12}>\n <OpenAIChat />\n </Grid>\n </Grid>\n </Container>\n </Box>\n </ThemeProvider>\n );\n}\n\nexport default App;\n","const reportWebVitals = (onPerfEntry) => {\n if (onPerfEntry && onPerfEntry instanceof Function) {\n import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {\n getCLS(onPerfEntry);\n getFID(onPerfEntry);\n getFCP(onPerfEntry);\n getLCP(onPerfEntry);\n getTTFB(onPerfEntry);\n });\n }\n};\n\nexport default reportWebVitals;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n document.getElementById('root')\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals();\n"],"sourceRoot":""}
 
 
static_bak_20250820_085943/static/js/runtime-main.25710301.js DELETED
@@ -1,2 +0,0 @@
1
- !function(e){function r(r){for(var n,i,a=r[0],c=r[1],l=r[2],p=0,s=[];p<a.length;p++)i=a[p],Object.prototype.hasOwnProperty.call(o,i)&&o[i]&&s.push(o[i][0]),o[i]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var c=t[a];0!==o[c]&&(n=!1)}n&&(u.splice(r--,1),e=i(i.s=t[0]))}return e}var n={},o={1:0},u=[];function i(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,i),t.l=!0,t.exports}i.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,a=document.createElement("script");a.charset="utf-8",a.timeout=120,i.nc&&a.setAttribute("nonce",i.nc),a.src=function(e){return i.p+"static/js/"+({}[e]||e)+"."+{3:"9013e23f"}[e]+".chunk.js"}(e);var c=new Error;u=function(r){a.onerror=a.onload=null,clearTimeout(l);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var l=setTimeout((function(){u({type:"timeout",target:a})}),12e4);a.onerror=a.onload=u,document.head.appendChild(a)}return Promise.all(r)},i.m=e,i.c=n,i.d=function(e,r,t){i.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,r){if(1&r&&(e=i(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(i.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)i.d(t,n,function(r){return e[r]}.bind(null,n));return t},i.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(r,"a",r),r},i.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},i.p="/",i.oe=function(e){throw console.error(e),e};var a=this["webpackJsonpvision-web-app"]=this["webpackJsonpvision-web-app"]||[],c=a.push.bind(a);a.push=r,a=a.slice();for(var l=0;l<a.length;l++)r(a[l]);var f=c;t()}([]);
2
- //# sourceMappingURL=runtime-main.25710301.js.map
 
 
 
static_bak_20250820_085943/static/js/runtime-main.25710301.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","1","exports","module","l","e","promises","installedChunkData","promise","Promise","resolve","reject","onScriptComplete","script","document","createElement","charset","timeout","nc","setAttribute","src","p","jsonpScriptSrc","error","Error","event","onerror","onload","clearTimeout","chunk","errorType","type","realSrc","target","message","name","request","undefined","setTimeout","head","appendChild","all","m","c","d","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","oe","err","console","jsonpArray","this","oldJsonpFunction","slice"],"mappings":"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","file":"static/js/runtime-main.25710301.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t1: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// script path function\n \tfunction jsonpScriptSrc(chunkId) {\n \t\treturn __webpack_require__.p + \"static/js/\" + ({}[chunkId]||chunkId) + \".\" + {\"3\":\"9013e23f\"}[chunkId] + \".chunk.js\"\n \t}\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n \t// This file contains only the entry chunk.\n \t// The chunk loading function for additional chunks\n \t__webpack_require__.e = function requireEnsure(chunkId) {\n \t\tvar promises = [];\n\n\n \t\t// JSONP chunk loading for javascript\n\n \t\tvar installedChunkData = installedChunks[chunkId];\n \t\tif(installedChunkData !== 0) { // 0 means \"already installed\".\n\n \t\t\t// a Promise means \"currently loading\".\n \t\t\tif(installedChunkData) {\n \t\t\t\tpromises.push(installedChunkData[2]);\n \t\t\t} else {\n \t\t\t\t// setup Promise in chunk cache\n \t\t\t\tvar promise = new Promise(function(resolve, reject) {\n \t\t\t\t\tinstalledChunkData = installedChunks[chunkId] = [resolve, reject];\n \t\t\t\t});\n \t\t\t\tpromises.push(installedChunkData[2] = promise);\n\n \t\t\t\t// start chunk loading\n \t\t\t\tvar script = document.createElement('script');\n \t\t\t\tvar onScriptComplete;\n\n \t\t\t\tscript.charset = 'utf-8';\n \t\t\t\tscript.timeout = 120;\n \t\t\t\tif (__webpack_require__.nc) {\n \t\t\t\t\tscript.setAttribute(\"nonce\", __webpack_require__.nc);\n \t\t\t\t}\n \t\t\t\tscript.src = jsonpScriptSrc(chunkId);\n\n \t\t\t\t// create error before stack unwound to get useful stacktrace later\n \t\t\t\tvar error = new Error();\n \t\t\t\tonScriptComplete = function (event) {\n \t\t\t\t\t// avoid mem leaks in IE.\n \t\t\t\t\tscript.onerror = script.onload = null;\n \t\t\t\t\tclearTimeout(timeout);\n \t\t\t\t\tvar chunk = installedChunks[chunkId];\n \t\t\t\t\tif(chunk !== 0) {\n \t\t\t\t\t\tif(chunk) {\n \t\t\t\t\t\t\tvar errorType = event && (event.type === 'load' ? 'missing' : event.type);\n \t\t\t\t\t\t\tvar realSrc = event && event.target && event.target.src;\n \t\t\t\t\t\t\terror.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';\n \t\t\t\t\t\t\terror.name = 'ChunkLoadError';\n \t\t\t\t\t\t\terror.type = errorType;\n \t\t\t\t\t\t\terror.request = realSrc;\n \t\t\t\t\t\t\tchunk[1](error);\n \t\t\t\t\t\t}\n \t\t\t\t\t\tinstalledChunks[chunkId] = undefined;\n \t\t\t\t\t}\n \t\t\t\t};\n \t\t\t\tvar timeout = setTimeout(function(){\n \t\t\t\t\tonScriptComplete({ type: 'timeout', target: script });\n \t\t\t\t}, 120000);\n \t\t\t\tscript.onerror = script.onload = onScriptComplete;\n \t\t\t\tdocument.head.appendChild(script);\n \t\t\t}\n \t\t}\n \t\treturn Promise.all(promises);\n \t};\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/\";\n\n \t// on error function for async loading\n \t__webpack_require__.oe = function(err) { console.error(err); throw err; };\n\n \tvar jsonpArray = this[\"webpackJsonpvision-web-app\"] = this[\"webpackJsonpvision-web-app\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// run deferred modules from other chunks\n \tcheckDeferredModules();\n"],"sourceRoot":""}
 
 
test_images/README.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Product Comparison API Testing Instructions
2
+
3
+ To test the product comparison API, place test images in this directory. The test images can be:
4
+
5
+ 1. Cars (e.g., car1.jpg, car2.jpg)
6
+ 2. Electronics like smartphones or laptops (e.g., phone1.jpg, phone2.jpg)
7
+ 3. Any other products you want to compare
8
+
9
+ ## Testing Steps
10
+
11
+ 1. Start the Flask app: `python app.py`
12
+ 2. In a separate terminal, run the test script:
13
+ `python test_product_comparison_api.py --images test_images/image1.jpg test_images/image2.jpg`
14
+
15
+ ## Expected Results
16
+
17
+ The test script will:
18
+ - Send images to the API endpoint
19
+ - Connect to the SSE stream to receive real-time updates
20
+ - Display the final analysis results
21
+
22
+ If successful, you should see structured output with product details, comparisons, and recommendations.
test_product_comparison_api.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Product Comparison API Test Script
4
+
5
+ This script tests the product comparison API endpoints with local image files.
6
+ It supports both the /api/product/compare/start and /api/product/compare/stream endpoints.
7
+
8
+ Requirements:
9
+ - pip install requests pillow sseclient-py
10
+
11
+ Usage:
12
+ python test_product_comparison_api.py --images path/to/image1.jpg path/to/image2.jpg
13
+
14
+ Example:
15
+ python test_product_comparison_api.py --images test_images/car1.jpg test_images/car2.jpg
16
+ """
17
+
18
+ import requests
19
+ import base64
20
+ import json
21
+ import time
22
+ import os
23
+ import sys
24
+ from io import BytesIO
25
+ from PIL import Image
26
+ import argparse
27
+
28
+ try:
29
+ import sseclient
30
+ except ImportError:
31
+ print("Missing required package: sseclient-py")
32
+ print("Please install with: pip install sseclient-py")
33
+ sys.exit(1)
34
+
35
+ def load_and_encode_image(image_path):
36
+ """Load an image file and convert to base64 string"""
37
+ try:
38
+ with Image.open(image_path) as img:
39
+ buffered = BytesIO()
40
+ img.save(buffered, format=img.format or "JPEG")
41
+ img_str = base64.b64encode(buffered.getvalue()).decode('utf-8')
42
+ return img_str
43
+ except Exception as e:
44
+ print(f"Error loading image {image_path}: {e}")
45
+ return None
46
+
47
+ def test_product_comparison_api(base_url, image_paths, timeout=300):
48
+ """Test the product comparison API with provided images"""
49
+ print(f"Testing product comparison API with {len(image_paths)} images")
50
+
51
+ # 1. Encode images as base64
52
+ images = []
53
+ for path in image_paths:
54
+ if not os.path.exists(path):
55
+ print(f"Error: Image file {path} not found")
56
+ return
57
+
58
+ encoded = load_and_encode_image(path)
59
+ if encoded:
60
+ images.append(encoded)
61
+ print(f"Successfully encoded image: {path}")
62
+
63
+ if not images:
64
+ print("No valid images to process")
65
+ return
66
+
67
+ # 2. Start a comparison session
68
+ start_url = f"{base_url}/api/product/compare/start"
69
+ print(f"Sending request to {start_url}")
70
+
71
+ try:
72
+ response = requests.post(
73
+ start_url,
74
+ json={"images": images}
75
+ )
76
+
77
+ if not response.ok:
78
+ print(f"Error starting comparison: HTTP {response.status_code}")
79
+ print(response.text)
80
+ return
81
+
82
+ data = response.json()
83
+ session_id = data.get('session_id')
84
+
85
+ if not session_id:
86
+ print("Error: No session ID received")
87
+ return
88
+
89
+ print(f"Session started with ID: {session_id}")
90
+ print(f"Status: {data.get('status')}")
91
+
92
+ # 3. Connect to the streaming endpoint
93
+ stream_url = f"{base_url}/api/product/compare/stream/{session_id}"
94
+ print(f"Connecting to stream at {stream_url}")
95
+
96
+ headers = {'Accept': 'text/event-stream'}
97
+ response = requests.get(stream_url, headers=headers, stream=True)
98
+
99
+ client = sseclient.SSEClient(response)
100
+
101
+ start_time = time.time()
102
+ result = None
103
+
104
+ print("\n--- Streaming Updates ---")
105
+ try:
106
+ for event in client.events():
107
+ current_time = time.time()
108
+ if current_time - start_time > timeout:
109
+ print("Timeout reached. Ending stream.")
110
+ break
111
+
112
+ try:
113
+ data = json.loads(event.data)
114
+
115
+ if 'message' in data:
116
+ print(f"Message: {data['message']}")
117
+
118
+ if 'status' in data:
119
+ print(f"Status update: {data['status']}")
120
+
121
+ if 'result' in data:
122
+ print("\n--- Final Results ---")
123
+ result = data['result']
124
+ print(json.dumps(result, indent=2))
125
+ break
126
+
127
+ if 'error' in data:
128
+ print(f"Error: {data['error']}")
129
+ break
130
+
131
+ except json.JSONDecodeError:
132
+ print(f"Error decoding JSON from event data: {event.data}")
133
+ continue
134
+ except KeyboardInterrupt:
135
+ print("Stream monitoring interrupted by user")
136
+
137
+ return result
138
+
139
+ except Exception as e:
140
+ print(f"Error in API test: {e}")
141
+ return None
142
+
143
+ if __name__ == "__main__":
144
+ parser = argparse.ArgumentParser(description='Test Product Comparison API')
145
+ parser.add_argument('--url', default='http://localhost:5000', help='Base URL for the API')
146
+ parser.add_argument('--images', nargs='+', required=True, help='Paths to images for testing')
147
+ parser.add_argument('--timeout', type=int, default=300, help='Timeout in seconds')
148
+
149
+ args = parser.parse_args()
150
+
151
+ test_product_comparison_api(args.url, args.images, args.timeout)