[email protected] commited on
Commit
f8d91ab
·
1 Parent(s): ed0f075

Add hf token support from UI

Browse files
amazon_this_object/__pycache__/__init__.cpython-312.pyc DELETED
Binary file (164 Bytes)
 
amazon_this_object/__pycache__/main.cpython-312.pyc DELETED
Binary file (4.04 kB)
 
amazon_this_object/main.py CHANGED
@@ -6,29 +6,104 @@ from reachy_mini.utils import create_head_pose
6
  import numpy as np
7
  import time
8
  import cv2
9
- from fastapi.responses import Response
10
  import os
11
  from openai import OpenAI
 
 
12
 
13
  class AmazonThisObject(ReachyMiniApp):
14
- # Optional: URL to a custom configuration page for the app
15
- # eg. "http://localhost:8042"
16
  custom_app_url: str | None = "http://0.0.0.0:8042"
17
 
 
 
 
 
 
 
 
 
 
18
  def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
19
  last_photo = None
20
  last_photo_timestamp = None
21
  image_url = f""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- hf_client = OpenAI(
24
- base_url="https://router.huggingface.co/v1",
25
- #define token in environment variable
26
- api_key=os.environ['HF_TOKEN']
27
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  #Take photo REST POST request
30
  @self.settings_app.post("/take_photo")
31
- def take_photo():
32
  print("Take a photo")
33
  nonlocal last_photo, last_photo_timestamp
34
 
@@ -67,13 +142,18 @@ class AmazonThisObject(ReachyMiniApp):
67
 
68
  #Get link REST POST request
69
  @self.settings_app.post("/get_link")
70
- def get_link():
71
  #Restore image from save variable last_photo
72
  b64_image = base64.b64encode(last_photo).decode('utf-8')
73
  image_url = f"data:image/jpeg;base64,{b64_image}"
74
 
75
  #Use model zai-org/GLM-4.5V with inference novita with huggingface client.
76
  #Analyse image and get URL only -> bypass blabla from LLM by usin "no blabla".
 
 
 
 
 
77
  completion = hf_client.chat.completions.create(
78
  model="zai-org/GLM-4.5V:novita",
79
  messages=[
 
6
  import numpy as np
7
  import time
8
  import cv2
 
9
  import os
10
  from openai import OpenAI
11
+ from pathlib import Path
12
+ from fastapi import Request, Depends, Body
13
 
14
  class AmazonThisObject(ReachyMiniApp):
 
 
15
  custom_app_url: str | None = "http://0.0.0.0:8042"
16
 
17
+ amaz_token: str = "" # ou la valeur que tu veux
18
+
19
+ def __init__(self, *args, **kwargs):
20
+ super().__init__(*args, **kwargs)
21
+ self.amaz_token = ""
22
+
23
+ def _get_self(self):
24
+ return self
25
+
26
  def run(self, reachy_mini: ReachyMini, stop_event: threading.Event):
27
  last_photo = None
28
  last_photo_timestamp = None
29
  image_url = f""
30
+ exec_dir = os.path.abspath(__file__)
31
+ exec_dir = exec_dir.replace("main.py","")
32
+ print("exec_dir "+exec_dir)
33
+ file_path = Path(exec_dir) / ".env"
34
+
35
+ @self.settings_app.post("/set_token")
36
+ async def set_hf_token(request: Request):
37
+ try:
38
+ data = await request.json()
39
+ token = data.get("token", "").strip()
40
+
41
+ if not token:
42
+ return {
43
+ "success": False,
44
+ "message": "No token provided."
45
+ }
46
 
47
+ # Chemin du fichier .env dans le répertoire d'exécution
48
+
49
+ # On écrase ou crée le fichier avec HF_TOKEN=ton_token
50
+ with open(file_path, "w") as f:
51
+ f.write(f"HF_TOKEN={token}\n")
52
+ print(f"Token Hugging Face successfully saved in {file_path}")
53
+ return {
54
+ "success": True,
55
+ "message": "Token saved."
56
+ }
57
+ except Exception as e:
58
+ return {
59
+ "success": False,
60
+ "message": "Error on server side"
61
+ }
62
+
63
+ @self.settings_app.post("/check_token") # ou .get() si tu préfères, c'est plus logique pour une vérif
64
+ async def hf_token_available(self_instance: AmazonThisObject = Depends(self._get_self)):
65
+ file_path = Path(exec_dir) / ".env"
66
+ if not file_path.exists():
67
+ print(".env n'existe pas, ce con n'a pas encore mis son token.")
68
+ return {
69
+ "success": False,
70
+ "token": None
71
+ }
72
+ try:
73
+ # On lit tout le fichier
74
+ with open(file_path, "r", encoding="utf-8") as f:
75
+ lines = f.readlines()
76
+ # On cherche la ligne HF_TOKEN=
77
+ for line in lines:
78
+ line = line.strip()
79
+ if line.startswith("HF_TOKEN="):
80
+ # On prend tout après le =
81
+ self_instance.amaz_token = line.split("=", 1)[1].strip().strip('"').strip("'")
82
+ break
83
+ if self_instance.amaz_token:
84
+ print("Token Hugging Face found. " + self_instance.amaz_token)
85
+ return {
86
+ "success": True,
87
+ "token": self_instance.amaz_token
88
+ }
89
+ else:
90
+ print("No HF_TOKEN found.")
91
+ return {
92
+ "success": False,
93
+ "token": None,
94
+ "message": "HF_TOKEN missing"
95
+ }
96
+ except Exception as e:
97
+ print(f"Errot while readind .env : {e}")
98
+ return {
99
+ "success": False,
100
+ "token": None,
101
+ "message": "Error whild reading file"
102
+ }
103
 
104
  #Take photo REST POST request
105
  @self.settings_app.post("/take_photo")
106
+ def take_photo(self_instance: AmazonThisObject = Depends(self._get_self)):
107
  print("Take a photo")
108
  nonlocal last_photo, last_photo_timestamp
109
 
 
142
 
143
  #Get link REST POST request
144
  @self.settings_app.post("/get_link")
145
+ def get_link(self_instance: AmazonThisObject = Depends(self._get_self)):
146
  #Restore image from save variable last_photo
147
  b64_image = base64.b64encode(last_photo).decode('utf-8')
148
  image_url = f"data:image/jpeg;base64,{b64_image}"
149
 
150
  #Use model zai-org/GLM-4.5V with inference novita with huggingface client.
151
  #Analyse image and get URL only -> bypass blabla from LLM by usin "no blabla".
152
+ hf_client = OpenAI(
153
+ base_url="https://router.huggingface.co/v1",
154
+ #define token in environment variable
155
+ api_key=self_instance.amaz_token
156
+ )
157
  completion = hf_client.chat.completions.create(
158
  model="zai-org/GLM-4.5V:novita",
159
  messages=[
amazon_this_object/static/index.html CHANGED
@@ -11,13 +11,17 @@
11
  <header>
12
  <h1>Amazon This Object</h1>
13
  </header>
14
-
15
- <main>
 
 
 
 
 
16
  <div class="preview-container">
17
  <img id="preview" src="" alt="Camera preview">
18
  <div class="no-image" id="no-image">No image yet</div>
19
  </div>
20
-
21
  <div class="controls">
22
  <button class="btn primary" id="take-btn">Capture object</button>
23
  <button class="btn primary" id="link-btn">Amazon this object</button>
@@ -26,7 +30,6 @@
26
  <div class="status" id="status"></div>
27
  </main>
28
  </div>
29
-
30
  <script src="/static/main.js"></script>
31
  </body>
32
- </html>
 
11
  <header>
12
  <h1>Amazon This Object</h1>
13
  </header>
14
+
15
+ <section class="token-section">
16
+ <input type="text" id="hf_token" placeholder="Enter HuggingFace token" />
17
+ <button class="btn primary" id="apply_token" type="button">Submit</button>
18
+ </section>
19
+
20
+ <main id="main_container">
21
  <div class="preview-container">
22
  <img id="preview" src="" alt="Camera preview">
23
  <div class="no-image" id="no-image">No image yet</div>
24
  </div>
 
25
  <div class="controls">
26
  <button class="btn primary" id="take-btn">Capture object</button>
27
  <button class="btn primary" id="link-btn">Amazon this object</button>
 
30
  <div class="status" id="status"></div>
31
  </main>
32
  </div>
 
33
  <script src="/static/main.js"></script>
34
  </body>
35
+ </html>
amazon_this_object/static/main.js CHANGED
@@ -4,9 +4,75 @@ const takeBtn = document.getElementById('take-btn');
4
  const linkBtn = document.getElementById('link-btn');
5
  const amazonLink = document.getElementById('amazon_link');
6
  const status = document.getElementById('status');
 
 
7
 
8
  let currentFilename = null;
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  function validURL(str) {
11
  var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
12
  '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
@@ -77,3 +143,6 @@ async function takePhoto() {
77
 
78
  takeBtn.addEventListener('click', takePhoto);
79
  linkBtn.addEventListener('click', getLink);
 
 
 
 
4
  const linkBtn = document.getElementById('link-btn');
5
  const amazonLink = document.getElementById('amazon_link');
6
  const status = document.getElementById('status');
7
+ const apply_token = document.getElementById("apply_token")
8
+ const hf_token = document.getElementById("hf_token")
9
 
10
  let currentFilename = null;
11
 
12
+ HF_TOKEN = ""
13
+
14
+ async function set_token() {
15
+ const token_value = hf_token.value.trim(); // On trim au cas où y'aurait des espaces de merde
16
+
17
+ if (!token_value) {
18
+ console.error("Token part is empty.");
19
+ return;
20
+ }
21
+
22
+ try {
23
+ const resp = await fetch('/set_token', {
24
+ method: 'POST',
25
+ headers: {
26
+ 'Content-Type': 'application/json'
27
+ },
28
+ body: JSON.stringify({ token: token_value }) // Voilà, on envoie le token, putain !
29
+ });
30
+
31
+ const data = await resp.json();
32
+
33
+ if (data.success) {
34
+ main_container.style.display = 'block';
35
+ console.log("Token recorded :", data);
36
+ // Tu peux ajouter un message à l'UI ici si tu veux
37
+ } else {
38
+ console.error("Error :", data.message || data);
39
+ }
40
+ } catch (error) {
41
+ console.error("Network error :", error);
42
+ }
43
+ }
44
+
45
+ async function check_token()
46
+ {
47
+ if( HF_TOKEN == "" )
48
+ {
49
+ const resp = await fetch('/check_token', { method: 'POST' });
50
+ const data = await resp.json();
51
+
52
+ console.log(data)
53
+
54
+ if( data.success )
55
+ {
56
+ main_container.style.display = 'block';
57
+ HF_TOKEN = data.token
58
+ hf_token.value = data.token
59
+ return true
60
+ }
61
+ else
62
+ {
63
+ main_container.style.display = 'none';
64
+ HF_TOKEN = ""
65
+ status.innerHTML = data.message
66
+ hf_token.value = ""
67
+ return false
68
+ }
69
+ }
70
+ else
71
+ {
72
+ return true
73
+ }
74
+ }
75
+
76
  function validURL(str) {
77
  var pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
78
  '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
 
143
 
144
  takeBtn.addEventListener('click', takePhoto);
145
  linkBtn.addEventListener('click', getLink);
146
+ apply_token.addEventListener('click', set_token);
147
+
148
+ check_token()
amazon_this_object/static/style.css CHANGED
@@ -1,101 +1,91 @@
1
- :root {
2
- --bg: #ffffff;
3
- --bg-secondary: #f5f5f5;
4
- --text: #111111;
5
- --text-muted: #888888;
6
- --border: #e0e0e0;
7
- --transition: 0.2s ease-in-out;
8
- }
9
-
10
- @media (prefers-color-scheme: dark) {
11
- :root {
12
- --bg: #121212;
13
- --bg-secondary: #1e1e1e;
14
- --text: #ffffff;
15
- --text-muted: #bbbbbb;
16
- --border: #333333;
17
- }
18
- }
19
-
20
  * {
 
21
  margin: 0;
22
  padding: 0;
23
- box-sizing: border-box;
24
  }
25
 
26
  body {
27
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
28
- background: var(--bg);
29
- color: var(--text);
30
  min-height: 100vh;
31
- font-size: 1rem; /* Base pour les rem */
 
 
32
  }
33
 
34
  .container {
35
- max-width: 52.5rem; /* 840px en rem (16px base) */
36
- margin: 0 auto;
37
- padding: 1.5rem 1rem;
38
- min-height: 100vh;
39
- display: flex;
40
- flex-direction: column;
 
41
  }
42
 
43
  header {
44
  text-align: center;
45
- margin-bottom: 1.5rem;
46
  }
47
 
48
- .logo {
49
- display: flex;
50
- align-items: center;
51
- justify-content: center;
52
- gap: 0.375rem;
53
- margin-bottom: 0.25rem;
54
  }
55
 
56
- .logo-icon {
57
- width: 0.25rem;
58
- height: 0.25rem;
59
- background: var(--text);
60
- border-radius: 50%;
61
  }
62
 
63
- .logo span {
64
- font-size: 0.5625rem; /* 9px */
65
- letter-spacing: 0.125rem;
66
- color: var(--text-muted);
67
- text-transform: uppercase;
 
 
 
 
68
  }
69
 
70
- h1 {
71
- font-size: 0.875rem; /* 14px */
72
- font-weight: 500;
73
- letter-spacing: 0.25rem;
74
- text-transform: uppercase;
75
  }
76
 
77
- main {
78
- flex: 1;
79
- display: flex;
80
- flex-direction: column;
81
- gap: 1rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
 
84
  .preview-container {
85
  position: relative;
86
- background: var(--bg-secondary);
87
- border: 1px solid var(--border);
88
- border-radius: 0.5rem;
89
- aspect-ratio: 4/3;
90
  overflow: hidden;
91
- display: flex;
92
- align-items: center;
93
- justify-content: center;
94
- transition: border-color var(--transition);
95
- }
96
-
97
- .preview-container:hover {
98
- border-color: var(--text-muted);
99
  }
100
 
101
  #preview {
@@ -106,71 +96,58 @@ main {
106
  }
107
 
108
  .no-image {
109
- font-size: 0.6875rem; /* 11px */
110
- color: var(--text-muted);
111
- text-transform: uppercase;
112
- letter-spacing: 0.0625rem;
 
 
113
  }
114
 
115
  .controls {
116
  display: flex;
117
- gap: 0.5rem;
118
- }
119
-
120
- .btn {
121
- flex: 1;
122
- padding: 0.75rem;
123
- border: 1px solid var(--border);
124
- border-radius: 0.25rem;
125
- font-family: inherit;
126
- font-size: 0.6875rem; /* 11px */
127
- letter-spacing: 0.0625rem;
128
- text-transform: uppercase;
129
- cursor: pointer;
130
- background: var(--bg);
131
- color: var(--text);
132
- transition: background var(--transition), opacity var(--transition);
133
- }
134
-
135
- .btn:hover {
136
- background: var(--bg-secondary);
137
- }
138
-
139
- .btn.primary {
140
- background: var(--text);
141
- border-color: var(--text);
142
- color: var(--bg);
143
  }
144
 
145
- .btn.primary:hover {
146
- opacity: 0.9;
147
- background: var(--text); /* Garde la couleur pour primary */
 
 
148
  }
149
 
150
- .btn:disabled {
151
- opacity: 0.4;
152
- cursor: not-allowed;
 
153
  }
154
 
155
- .btn:focus {
156
- outline: none;
157
- box-shadow: 0 0 0 0.125rem rgba(0, 0, 0, 0.2); /* Améliore l'accessibilité */
158
  }
159
 
160
- .status {
161
- text-align: center;
162
- font-size: 0.6875rem; /* 11px */
163
- color: var(--text-muted);
164
- min-height: 1rem;
165
  }
166
 
167
- /* Media query pour plus de responsivité */
168
- @media (min-width: 48rem) { /* 768px */
169
  .container {
170
- padding: 2rem;
171
  }
172
-
173
  h1 {
174
- font-size: 1rem;
 
 
 
 
 
 
 
 
 
175
  }
176
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  * {
2
+ box-sizing: border-box;
3
  margin: 0;
4
  padding: 0;
 
5
  }
6
 
7
  body {
8
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
9
+ background-color: #121212;
10
+ color: #e0e0e0;
11
  min-height: 100vh;
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
  }
16
 
17
  .container {
18
+ width: 90%;
19
+ max-width: 500px;
20
+ background-color: #1e1e1e;
21
+ border-radius: 16px;
22
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
23
+ overflow: hidden;
24
+ padding: 20px;
25
  }
26
 
27
  header {
28
  text-align: center;
29
+ margin-bottom: 30px;
30
  }
31
 
32
+ h1 {
33
+ font-size: 2rem;
34
+ color: #ff9900; /* Orange Amazon, putain de classe */
35
+ text-shadow: 0 2px 4px rgba(0,0,0,0.3);
 
 
36
  }
37
 
38
+ .token-section {
39
+ margin-bottom: 30px;
40
+ text-align: center;
 
 
41
  }
42
 
43
+ input[type="text"] {
44
+ width: 100%;
45
+ padding: 12px 16px;
46
+ margin-bottom: 15px;
47
+ background-color: #2d2d2d;
48
+ border: 1px solid #444;
49
+ border-radius: 8px;
50
+ color: #fff;
51
+ font-size: 1rem;
52
  }
53
 
54
+ input[type="text"]::placeholder {
55
+ color: #888;
 
 
 
56
  }
57
 
58
+ .btn {
59
+ padding: 12px 24px;
60
+ border: none;
61
+ border-radius: 8px;
62
+ font-size: 1rem;
63
+ font-weight: bold;
64
+ cursor: pointer;
65
+ transition: all 0.3s ease;
66
+ margin: 10px 5px;
67
+ }
68
+
69
+ .btn.primary {
70
+ background-color: #ff9900;
71
+ color: #000;
72
+ }
73
+
74
+ .btn.primary:hover {
75
+ background-color: #ffb84d;
76
+ transform: translateY(-2px);
77
+ box-shadow: 0 4px 10px rgba(255, 153, 0, 0.4);
78
  }
79
 
80
  .preview-container {
81
  position: relative;
82
+ width: 100%;
83
+ height: 300px;
84
+ background-color: #000;
85
+ border-radius: 12px;
86
  overflow: hidden;
87
+ margin-bottom: 20px;
88
+ border: 2px solid #333;
 
 
 
 
 
 
89
  }
90
 
91
  #preview {
 
96
  }
97
 
98
  .no-image {
99
+ position: absolute;
100
+ top: 50%;
101
+ left: 50%;
102
+ transform: translate(-50%, -50%);
103
+ font-size: 1.2rem;
104
+ color: #666;
105
  }
106
 
107
  .controls {
108
  display: flex;
109
+ justify-content: center;
110
+ flex-wrap: wrap;
111
+ margin-bottom: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  }
113
 
114
+ .status {
115
+ text-align: center;
116
+ padding: 10px;
117
+ font-size: 1.1rem;
118
+ min-height: 30px;
119
  }
120
 
121
+ #amazon_link {
122
+ margin-top: 20px;
123
+ font-weight: bold;
124
+ word-break: break-all;
125
  }
126
 
127
+ #amazon_link a {
128
+ color: #ff9900;
129
+ text-decoration: none;
130
  }
131
 
132
+ #amazon_link a:hover {
133
+ text-decoration: underline;
 
 
 
134
  }
135
 
136
+ @media (max-width: 480px) {
 
137
  .container {
138
+ padding: 15px;
139
  }
140
+
141
  h1 {
142
+ font-size: 1.8rem;
143
+ }
144
+
145
+ .btn {
146
+ width: 100%;
147
+ margin: 8px 0;
148
+ }
149
+
150
+ .controls {
151
+ flex-direction: column;
152
  }
153
  }
pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
 
5
  [project]
6
  name = "amazon-this-object"
7
- version = "0.1.0"
8
  description = "Get a link on Amazon, to buy a similar object you have."
9
  readme = "README.md"
10
  requires-python = ">=3.10"
 
4
 
5
  [project]
6
  name = "amazon-this-object"
7
+ version = "0.1.1"
8
  description = "Get a link on Amazon, to buy a similar object you have."
9
  readme = "README.md"
10
  requires-python = ">=3.10"
style.css CHANGED
@@ -48,7 +48,7 @@ body {
48
  }
49
 
50
  .container {
51
- max-width: 640px;
52
  margin: 0 auto;
53
  padding: 0 2rem;
54
  position: relative;
@@ -408,4 +408,4 @@ body {
408
  .download-options {
409
  grid-template-columns: 1fr;
410
  }
411
- }
 
48
  }
49
 
50
  .container {
51
+ max-width: 1200px;
52
  margin: 0 auto;
53
  padding: 0 2rem;
54
  position: relative;
 
408
  .download-options {
409
  grid-template-columns: 1fr;
410
  }
411
+ }