eduardo4547 commited on
Commit
f76ffb8
·
verified ·
1 Parent(s): ba4775f

Upload 306 files

Browse files
Files changed (40) hide show
  1. .gitattributes +10 -0
  2. backend/__pycache__/main.cpython-312.pyc +0 -0
  3. backend/logs/app.log +178 -0
  4. backend/main.py +52 -3
  5. backend/routers/__pycache__/auth.cpython-312.pyc +0 -0
  6. backend/routers/__pycache__/openai_image.cpython-312.pyc +0 -0
  7. backend/routers/auth.py +40 -7
  8. backend/routers/openai_image.py +114 -4
  9. backend/services/__pycache__/openai_service.cpython-312.pyc +0 -0
  10. backend/services/openai_service.py +74 -94
  11. backend/uploads/0489e41ca42c4f62972f9df4048f4fee.jpg +0 -0
  12. backend/uploads/0d38e64424a44bfea0d2cd60b6ab7ca7.jpg +0 -0
  13. backend/uploads/44551e85423f44e28220e796e8aad972.jpg +0 -0
  14. backend/uploads/6aae686070084421be72844fcab3432c.jpg +0 -0
  15. backend/uploads/91484306cbd94cc187cf2666993e17fd.jpg +0 -0
  16. backend/uploads/977ec30d99304e2a95328c448b8e71f7.jpg +0 -0
  17. backend/uploads/99256e3e599c4ed0ab1cfb9ed3823137.jpg +0 -0
  18. backend/uploads/b60b62ea9b67460d9f192fc7e46f0017.jpg +0 -0
  19. backend/uploads/db11756ac9d04460a06785f066342331.jpg +0 -0
  20. backend/uploads/f652de6149aa4fd6a775c770e5736367.jpg +0 -0
  21. backend/uploads/f8a0db84234c426e9252ee65e958f77a.jpg +0 -0
  22. backend/uploads/generated/032d76c129a442cebeac3323bc4fc080.png +3 -0
  23. backend/uploads/generated/08a1798e9521455a902edda85dc029cc.png +3 -0
  24. backend/uploads/generated/1bc4caa711694fb8acca6b23159f754f.png +3 -0
  25. backend/uploads/generated/231900b690ad4adb9883a3d600716a7d.png +3 -0
  26. backend/uploads/generated/2a5b913b68a243379027cd01b187b4cd.png +3 -0
  27. backend/uploads/generated/2d6e02255c3a4f00804ace65c49f19aa.png +3 -0
  28. backend/uploads/generated/5b6f11b554f74c5486ce33e390f8c75d.png +3 -0
  29. backend/uploads/generated/836b20819d49430a9fc94c02e8e07885.png +3 -0
  30. backend/uploads/generated/d664c83e3b614760834cdaad28769340.png +3 -0
  31. backend/uploads/generated/dbd083972cf5430998885a9f7c36ac0a.png +3 -0
  32. frontend/dist/assets/index-CenEQfA2.css +1 -0
  33. frontend/dist/assets/index-DE9xapIa.js +0 -0
  34. frontend/dist/index.html +11 -4
  35. frontend/index.html +9 -2
  36. frontend/src/features/roomSetup/RoomSetup.tsx +19 -3
  37. frontend/src/features/roomSetup/roomSetupHooks.ts +17 -0
  38. frontend/src/features/roomVisualizer/RoomVisualizer.tsx +67 -1
  39. frontend/src/hooks/useApplyTexture.ts +21 -2
  40. frontend/src/version.ts +1 -1
.gitattributes CHANGED
@@ -162,3 +162,13 @@ backend/uploads/acm[[:space:]]gris_edit_19d266a8.jpg filter=lfs diff=lfs merge=l
162
  backend/uploads/acm[[:space:]]gris.jpg filter=lfs diff=lfs merge=lfs -text
163
  backend/uploads/hyper-reality-1778254222964_edit_9c5de50a_edit_7e5043d5.jpg filter=lfs diff=lfs merge=lfs -text
164
  backend/uploads/hyper-reality-1778254222964_edit_a92f4266_edit_161ee5c4.jpg filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
162
  backend/uploads/acm[[:space:]]gris.jpg filter=lfs diff=lfs merge=lfs -text
163
  backend/uploads/hyper-reality-1778254222964_edit_9c5de50a_edit_7e5043d5.jpg filter=lfs diff=lfs merge=lfs -text
164
  backend/uploads/hyper-reality-1778254222964_edit_a92f4266_edit_161ee5c4.jpg filter=lfs diff=lfs merge=lfs -text
165
+ backend/uploads/generated/032d76c129a442cebeac3323bc4fc080.png filter=lfs diff=lfs merge=lfs -text
166
+ backend/uploads/generated/08a1798e9521455a902edda85dc029cc.png filter=lfs diff=lfs merge=lfs -text
167
+ backend/uploads/generated/1bc4caa711694fb8acca6b23159f754f.png filter=lfs diff=lfs merge=lfs -text
168
+ backend/uploads/generated/231900b690ad4adb9883a3d600716a7d.png filter=lfs diff=lfs merge=lfs -text
169
+ backend/uploads/generated/2a5b913b68a243379027cd01b187b4cd.png filter=lfs diff=lfs merge=lfs -text
170
+ backend/uploads/generated/2d6e02255c3a4f00804ace65c49f19aa.png filter=lfs diff=lfs merge=lfs -text
171
+ backend/uploads/generated/5b6f11b554f74c5486ce33e390f8c75d.png filter=lfs diff=lfs merge=lfs -text
172
+ backend/uploads/generated/836b20819d49430a9fc94c02e8e07885.png filter=lfs diff=lfs merge=lfs -text
173
+ backend/uploads/generated/d664c83e3b614760834cdaad28769340.png filter=lfs diff=lfs merge=lfs -text
174
+ backend/uploads/generated/dbd083972cf5430998885a9f7c36ac0a.png filter=lfs diff=lfs merge=lfs -text
backend/__pycache__/main.cpython-312.pyc CHANGED
Binary files a/backend/__pycache__/main.cpython-312.pyc and b/backend/__pycache__/main.cpython-312.pyc differ
 
backend/logs/app.log CHANGED
@@ -5026,3 +5026,181 @@ RuntimeError: Both Gradio Spaces failed.
5026
  2026-05-11 18:47:40,240 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5027
  2026-05-11 18:47:53,426 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0278b94a12dd2c11934074, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0278b94a12dd2c11934074"}, "remainingTimeMS": 30}
5028
  2026-05-11 18:47:53,426 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0278b94a12dd2c11934074, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0278b94a12dd2c11934074"}, "remainingTimeMS": 30}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5026
  2026-05-11 18:47:40,240 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5027
  2026-05-11 18:47:53,426 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0278b94a12dd2c11934074, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0278b94a12dd2c11934074"}, "remainingTimeMS": 30}
5028
  2026-05-11 18:47:53,426 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0278b94a12dd2c11934074, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0278b94a12dd2c11934074"}, "remainingTimeMS": 30}
5029
+ 2026-05-11 19:06:05,368 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027cfda87b46fdcade70c0, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027cfda87b46fdcade70c0"}, "remainingTimeMS": 30}
5030
+ 2026-05-11 19:06:12,738 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a027d04a87b46fdcade70c1, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027d04a87b46fdcade70c1"}, "remainingTimeMS": 30}
5031
+ 2026-05-11 19:06:12,739 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a027d04a87b46fdcade70c1, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027d04a87b46fdcade70c1"}, "remainingTimeMS": 30}
5032
+ 2026-05-11 19:08:06,520 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027d761ac5024147e14ddd, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027d761ac5024147e14ddd"}, "remainingTimeMS": 30}
5033
+ 2026-05-11 19:08:47,155 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a027d9f1ac5024147e14dde, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027d9f1ac5024147e14dde"}, "remainingTimeMS": 30}
5034
+ 2026-05-11 19:08:47,218 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a027d9f1ac5024147e14dde, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027d9f1ac5024147e14dde"}, "remainingTimeMS": 30}
5035
+ 2026-05-11 19:09:25,574 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027dc593faabe7b94d01db, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027dc593faabe7b94d01db"}, "remainingTimeMS": 30}
5036
+ 2026-05-11 19:09:28,759 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027dc83e594b87cb65fae1, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027dc83e594b87cb65fae1"}, "remainingTimeMS": 30}
5037
+ 2026-05-11 19:11:42,248 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027e4e8ae5a4bdf2a01b4a, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027e4e8ae5a4bdf2a01b4a"}, "remainingTimeMS": 30}
5038
+ 2026-05-11 19:11:45,646 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027e511d40e5545928f21a, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027e511d40e5545928f21a"}, "remainingTimeMS": 30}
5039
+ 2026-05-11 19:12:29,155 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027e7d6b97ebcd8799b29a, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027e7d6b97ebcd8799b29a"}, "remainingTimeMS": 30}
5040
+ 2026-05-11 19:12:32,505 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027e803a5d599a0d8af5ec, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027e803a5d599a0d8af5ec"}, "remainingTimeMS": 30}
5041
+ 2026-05-11 19:12:38,840 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027e868bf43995e2ac9977, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027e868bf43995e2ac9977"}, "remainingTimeMS": 30}
5042
+ 2026-05-11 19:15:15,838 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027f23949e3a6713885bd3, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027f23949e3a6713885bd3"}, "remainingTimeMS": 30}
5043
+ 2026-05-11 19:15:16,974 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027f24949e3a6713885bd4, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027f24949e3a6713885bd4"}, "remainingTimeMS": 30}
5044
+ 2026-05-11 19:15:51,653 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027f470f7d0c4f53d759f5, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027f470f7d0c4f53d759f5"}, "remainingTimeMS": 30}
5045
+ 2026-05-11 19:15:52,590 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027f480f7d0c4f53d759f6, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027f480f7d0c4f53d759f6"}, "remainingTimeMS": 30}
5046
+ 2026-05-11 19:17:39,169 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027fb35cc9f5b422b27472, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027fb35cc9f5b422b27472"}, "remainingTimeMS": 30}
5047
+ 2026-05-11 19:17:40,232 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027fb45cc9f5b422b27473, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027fb45cc9f5b422b27473"}, "remainingTimeMS": 30}
5048
+ 2026-05-11 19:17:43,584 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027fb7568814d4963dd0bb, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027fb7568814d4963dd0bb"}, "remainingTimeMS": 30}
5049
+ 2026-05-11 19:17:44,583 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027fb8568814d4963dd0bc, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027fb8568814d4963dd0bc"}, "remainingTimeMS": 30}
5050
+ 2026-05-11 19:18:49,835 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027ff9c4a2c8fb7771ae26, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027ff9c4a2c8fb7771ae26"}, "remainingTimeMS": 29}
5051
+ 2026-05-11 19:18:51,007 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a027ffbc4a2c8fb7771ae27, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a027ffbc4a2c8fb7771ae27"}, "remainingTimeMS": 30}
5052
+ 2026-05-11 19:19:00,509 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028004c4a2c8fb7771ae28, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028004c4a2c8fb7771ae28"}, "remainingTimeMS": 30}
5053
+ 2026-05-11 19:19:34,358 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5054
+ 2026-05-11 19:20:25,698 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5055
+ 2026-05-11 19:23:23,468 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02810b6e10f71b8416429a, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02810b6e10f71b8416429a"}, "remainingTimeMS": 30}
5056
+ 2026-05-11 19:23:24,520 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02810c6e10f71b8416429b, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02810c6e10f71b8416429b"}, "remainingTimeMS": 30}
5057
+ 2026-05-11 19:25:05,589 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02817124161d08478a156b, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02817124161d08478a156b"}, "remainingTimeMS": 30}
5058
+ 2026-05-11 19:25:06,714 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02817224161d08478a156c, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02817224161d08478a156c"}, "remainingTimeMS": 30}
5059
+ 2026-05-11 19:25:28,914 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5060
+ 2026-05-11 19:26:13,818 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5061
+ 2026-05-11 19:26:21,775 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0281bd5ba8caf5a831ae90, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0281bd5ba8caf5a831ae90"}, "remainingTimeMS": 30}
5062
+ 2026-05-11 19:26:22,789 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0281be5ba8caf5a831ae91, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0281be5ba8caf5a831ae91"}, "remainingTimeMS": 30}
5063
+ 2026-05-11 19:36:28,448 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02841c703779c67f0d9b34, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02841c703779c67f0d9b34"}, "remainingTimeMS": 30}
5064
+ 2026-05-11 19:36:29,570 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02841d703779c67f0d9b35, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02841d703779c67f0d9b35"}, "remainingTimeMS": 30}
5065
+ 2026-05-11 19:36:33,261 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028421703779c67f0d9b36, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028421703779c67f0d9b36"}, "remainingTimeMS": 30}
5066
+ 2026-05-11 19:36:33,261 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028421703779c67f0d9b36, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028421703779c67f0d9b36"}, "remainingTimeMS": 30}
5067
+ 2026-05-11 19:37:02,209 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5068
+ 2026-05-11 19:39:05,683 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0284b9c02b8a2b4c1fd305, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0284b9c02b8a2b4c1fd305"}, "remainingTimeMS": 30}
5069
+ 2026-05-11 19:39:09,088 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0284bd757457c966632fb6, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0284bd757457c966632fb6"}, "remainingTimeMS": 30}
5070
+ 2026-05-11 19:39:10,088 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0284be757457c966632fb7, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0284be757457c966632fb7"}, "remainingTimeMS": 30}
5071
+ 2026-05-11 19:39:52,700 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0284e8757457c966632fb8, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0284e8757457c966632fb8"}, "remainingTimeMS": 30}
5072
+ 2026-05-11 19:39:52,701 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0284e8757457c966632fb8, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0284e8757457c966632fb8"}, "remainingTimeMS": 30}
5073
+ 2026-05-11 19:41:00,305 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02852c8a1367122245eccc, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02852c8a1367122245eccc"}, "remainingTimeMS": 30}
5074
+ 2026-05-11 19:41:03,225 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02852f267122fd694f21e3, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02852f267122fd694f21e3"}, "remainingTimeMS": 30}
5075
+ 2026-05-11 19:41:04,203 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028530267122fd694f21e4, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028530267122fd694f21e4"}, "remainingTimeMS": 30}
5076
+ 2026-05-11 19:41:41,968 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0285554c2f7ccbf3415908, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0285554c2f7ccbf3415908"}, "remainingTimeMS": 30}
5077
+ 2026-05-11 19:41:42,925 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0285564c2f7ccbf3415909, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0285564c2f7ccbf3415909"}, "remainingTimeMS": 30}
5078
+ 2026-05-11 19:42:05,869 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02856dac866a27c0a614ce, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02856dac866a27c0a614ce"}, "remainingTimeMS": 30}
5079
+ 2026-05-11 19:42:07,090 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02856fac866a27c0a614cf, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02856fac866a27c0a614cf"}, "remainingTimeMS": 30}
5080
+ 2026-05-11 19:42:08,500 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028570ac866a27c0a614d0, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028570ac866a27c0a614d0"}, "remainingTimeMS": 30}
5081
+ 2026-05-11 19:42:08,500 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028570ac866a27c0a614d0, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028570ac866a27c0a614d0"}, "remainingTimeMS": 30}
5082
+ 2026-05-11 19:42:45,966 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02859572403ff4d0782b8b, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02859572403ff4d0782b8b"}, "remainingTimeMS": 30}
5083
+ 2026-05-11 19:42:46,913 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02859672403ff4d0782b8c, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02859672403ff4d0782b8c"}, "remainingTimeMS": 30}
5084
+ 2026-05-11 19:42:59,118 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0285a372403ff4d0782b8d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0285a372403ff4d0782b8d"}, "remainingTimeMS": 30}
5085
+ 2026-05-11 19:42:59,120 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0285a372403ff4d0782b8d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0285a372403ff4d0782b8d"}, "remainingTimeMS": 30}
5086
+ 2026-05-11 19:43:13,311 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5087
+ 2026-05-11 19:43:33,906 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5088
+ 2026-05-11 19:48:53,424 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028705348962251040020b, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028705348962251040020b"}, "remainingTimeMS": 30}
5089
+ 2026-05-11 19:48:54,613 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028706348962251040020c, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028706348962251040020c"}, "remainingTimeMS": 30}
5090
+ 2026-05-11 19:48:56,049 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028708348962251040020d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028708348962251040020d"}, "remainingTimeMS": 30}
5091
+ 2026-05-11 19:48:56,050 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028708348962251040020d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028708348962251040020d"}, "remainingTimeMS": 30}
5092
+ 2026-05-11 19:48:56,051 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028708348962251040020d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028708348962251040020d"}, "remainingTimeMS": 30}
5093
+ 2026-05-11 19:49:15,705 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5094
+ 2026-05-11 19:49:38,447 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5095
+ 2026-05-11 19:51:04,940 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0287887f7d678f206594ba, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0287887f7d678f206594ba"}, "remainingTimeMS": 30}
5096
+ 2026-05-11 19:51:07,563 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02878bd029f8915c55e732, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02878bd029f8915c55e732"}, "remainingTimeMS": 30}
5097
+ 2026-05-11 19:51:08,470 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02878cd029f8915c55e733, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02878cd029f8915c55e733"}, "remainingTimeMS": 30}
5098
+ 2026-05-11 19:51:28,857 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5099
+ 2026-05-11 19:51:49,286 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5100
+ 2026-05-11 19:54:05,238 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02883d15dd6dc3e4f0f0b1, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02883d15dd6dc3e4f0f0b1"}, "remainingTimeMS": 30}
5101
+ 2026-05-11 19:54:06,288 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02883e15dd6dc3e4f0f0b2, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02883e15dd6dc3e4f0f0b2"}, "remainingTimeMS": 30}
5102
+ 2026-05-11 19:54:36,036 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02885c3027b040c1692ec0, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02885c3027b040c1692ec0"}, "remainingTimeMS": 30}
5103
+ 2026-05-11 19:54:37,006 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02885d3027b040c1692ec1, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02885d3027b040c1692ec1"}, "remainingTimeMS": 30}
5104
+ 2026-05-11 19:54:42,945 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0288623027b040c1692ec2, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288623027b040c1692ec2"}, "remainingTimeMS": 30}
5105
+ 2026-05-11 19:54:42,945 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0288623027b040c1692ec2, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288623027b040c1692ec2"}, "remainingTimeMS": 30}
5106
+ 2026-05-11 19:54:47,661 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5107
+ 2026-05-11 19:55:09,379 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5108
+ 2026-05-11 19:56:05,697 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0288b57400933c876eea54, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288b57400933c876eea54"}, "remainingTimeMS": 30}
5109
+ 2026-05-11 19:56:06,855 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0288b67400933c876eea55, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288b67400933c876eea55"}, "remainingTimeMS": 30}
5110
+ 2026-05-11 19:56:16,706 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0288c07400933c876eea56, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288c07400933c876eea56"}, "remainingTimeMS": 30}
5111
+ 2026-05-11 19:56:16,707 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0288c07400933c876eea56, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288c07400933c876eea56"}, "remainingTimeMS": 30}
5112
+ 2026-05-11 19:56:28,793 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0288ccf6c189e773fd6c5d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288ccf6c189e773fd6c5d"}, "remainingTimeMS": 30}
5113
+ 2026-05-11 19:56:29,776 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0288cdf6c189e773fd6c5e, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0288cdf6c189e773fd6c5e"}, "remainingTimeMS": 30}
5114
+ 2026-05-11 19:56:34,696 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5115
+ 2026-05-11 19:56:59,964 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5116
+ 2026-05-11 19:58:10,776 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028932e1953cbd31f51412, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028932e1953cbd31f51412"}, "remainingTimeMS": 30}
5117
+ 2026-05-11 19:58:11,857 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028933e1953cbd31f51413, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028933e1953cbd31f51413"}, "remainingTimeMS": 30}
5118
+ 2026-05-11 19:58:53,417 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02895dafb540fa4caa5bd7, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02895dafb540fa4caa5bd7"}, "remainingTimeMS": 30}
5119
+ 2026-05-11 19:58:56,404 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0289608155a426c77e36e5, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0289608155a426c77e36e5"}, "remainingTimeMS": 30}
5120
+ 2026-05-11 19:58:57,390 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0289618155a426c77e36e6, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0289618155a426c77e36e6"}, "remainingTimeMS": 30}
5121
+ 2026-05-11 19:59:02,129 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0289668155a426c77e36e7, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0289668155a426c77e36e7"}, "remainingTimeMS": 30}
5122
+ 2026-05-11 19:59:02,130 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a0289668155a426c77e36e7, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0289668155a426c77e36e7"}, "remainingTimeMS": 30}
5123
+ 2026-05-11 19:59:07,982 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5124
+ 2026-05-11 19:59:08,264 INFO backend.segmentation: OpenAI prompt length=919
5125
+ 2026-05-11 19:59:28,536 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5126
+ 2026-05-11 20:00:00,304 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0289a0d1507d375ff12f71, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0289a0d1507d375ff12f71"}, "remainingTimeMS": 30}
5127
+ 2026-05-11 20:00:01,492 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a0289a1d1507d375ff12f72, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0289a1d1507d375ff12f72"}, "remainingTimeMS": 30}
5128
+ 2026-05-11 20:00:04,295 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5129
+ 2026-05-11 20:00:28,052 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5130
+ 2026-05-11 20:00:28,570 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5131
+ 2026-05-11 20:00:43,336 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5132
+ 2026-05-11 20:01:48,552 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5133
+ 2026-05-11 20:02:03,110 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5134
+ 2026-05-11 20:03:03,014 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028a578f37a4f8e8d7d66c, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028a578f37a4f8e8d7d66c"}, "remainingTimeMS": 30}
5135
+ 2026-05-11 20:03:04,041 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028a588f37a4f8e8d7d66d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028a588f37a4f8e8d7d66d"}, "remainingTimeMS": 30}
5136
+ 2026-05-11 20:03:05,250 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028a598f37a4f8e8d7d66e, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028a598f37a4f8e8d7d66e"}, "remainingTimeMS": 30}
5137
+ 2026-05-11 20:03:05,250 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028a598f37a4f8e8d7d66e, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028a598f37a4f8e8d7d66e"}, "remainingTimeMS": 30}
5138
+ 2026-05-11 20:03:16,863 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5139
+ 2026-05-11 20:03:31,328 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5140
+ 2026-05-11 20:04:42,487 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5141
+ 2026-05-11 20:04:58,256 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5142
+ 2026-05-11 20:06:22,491 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028b1ebc16a0851415bf50, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028b1ebc16a0851415bf50"}, "remainingTimeMS": 29}
5143
+ 2026-05-11 20:06:23,537 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028b1fbc16a0851415bf51, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028b1fbc16a0851415bf51"}, "remainingTimeMS": 30}
5144
+ 2026-05-11 20:06:47,110 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028b37a802942fd2594f91, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028b37a802942fd2594f91"}, "remainingTimeMS": 30}
5145
+ 2026-05-11 20:06:49,908 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028b397b0f25a542e65c27, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028b397b0f25a542e65c27"}, "remainingTimeMS": 30}
5146
+ 2026-05-11 20:06:50,920 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028b3a7b0f25a542e65c28, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028b3a7b0f25a542e65c28"}, "remainingTimeMS": 30}
5147
+ 2026-05-11 20:11:50,807 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028c66b34267fbde5b34af, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028c66b34267fbde5b34af"}, "remainingTimeMS": 30}
5148
+ 2026-05-11 20:11:51,851 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028c67b34267fbde5b34b0, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028c67b34267fbde5b34b0"}, "remainingTimeMS": 30}
5149
+ 2026-05-11 20:13:05,234 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028cb1215d719b596e53d7, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028cb1215d719b596e53d7"}, "remainingTimeMS": 30}
5150
+ 2026-05-11 20:13:06,341 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028cb2215d719b596e53d8, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028cb2215d719b596e53d8"}, "remainingTimeMS": 30}
5151
+ 2026-05-11 20:15:36,865 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028d488e96660a60b8cfc4, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028d488e96660a60b8cfc4"}, "remainingTimeMS": 30}
5152
+ 2026-05-11 20:15:37,926 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028d498e96660a60b8cfc5, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028d498e96660a60b8cfc5"}, "remainingTimeMS": 30}
5153
+ 2026-05-11 20:15:39,983 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028d4b8e96660a60b8cfc6, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028d4b8e96660a60b8cfc6"}, "remainingTimeMS": 30}
5154
+ 2026-05-11 20:15:39,983 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028d4b8e96660a60b8cfc6, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028d4b8e96660a60b8cfc6"}, "remainingTimeMS": 30}
5155
+ 2026-05-11 20:15:47,786 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5156
+ 2026-05-11 20:16:09,295 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5157
+ 2026-05-11 20:16:09,669 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\2d6e02255c3a4f00804ace65c49f19aa.png
5158
+ 2026-05-11 20:16:09,844 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x0000029BEA759D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a028d698e96660a60b8cfc7, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028d698e96660a60b8cfc7"}, "remainingTimeMS": 30}
5159
+ 2026-05-11 20:20:08,586 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028e58878a8628bfee7a9c, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028e58878a8628bfee7a9c"}, "remainingTimeMS": 30}
5160
+ 2026-05-11 20:20:09,725 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028e59878a8628bfee7a9d, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028e59878a8628bfee7a9d"}, "remainingTimeMS": 30}
5161
+ 2026-05-11 20:20:11,253 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028e5b878a8628bfee7a9e, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028e5b878a8628bfee7a9e"}, "remainingTimeMS": 30}
5162
+ 2026-05-11 20:20:11,255 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028e5b878a8628bfee7a9e, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028e5b878a8628bfee7a9e"}, "remainingTimeMS": 30}
5163
+ 2026-05-11 20:20:16,995 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5164
+ 2026-05-11 20:20:33,482 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5165
+ 2026-05-11 20:20:34,342 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\231900b690ad4adb9883a3d600716a7d.png
5166
+ 2026-05-11 20:20:34,542 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001E117FD9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a028e72878a8628bfee7a9f, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028e72878a8628bfee7a9f"}, "remainingTimeMS": 30}
5167
+ 2026-05-11 20:20:46,915 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5168
+ 2026-05-11 20:21:02,580 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5169
+ 2026-05-11 20:21:02,987 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\d664c83e3b614760834cdaad28769340.png
5170
+ 2026-05-11 20:21:03,168 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001E117FD9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a028e8f878a8628bfee7aa0, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028e8f878a8628bfee7aa0"}, "remainingTimeMS": 30}
5171
+ 2026-05-11 20:24:43,167 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028f6bdd1d2a0373a94f3c, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028f6bdd1d2a0373a94f3c"}, "remainingTimeMS": 30}
5172
+ 2026-05-11 20:24:46,257 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028f6ef42c431356712eb2, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028f6ef42c431356712eb2"}, "remainingTimeMS": 30}
5173
+ 2026-05-11 20:24:47,309 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a028f6ff42c431356712eb3, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028f6ff42c431356712eb3"}, "remainingTimeMS": 30}
5174
+ 2026-05-11 20:24:48,513 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028f70f42c431356712eb4, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028f70f42c431356712eb4"}, "remainingTimeMS": 30}
5175
+ 2026-05-11 20:24:48,514 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a028f70f42c431356712eb4, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028f70f42c431356712eb4"}, "remainingTimeMS": 30}
5176
+ 2026-05-11 20:24:50,217 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5177
+ 2026-05-11 20:25:09,782 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5178
+ 2026-05-11 20:25:10,153 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\5b6f11b554f74c5486ce33e390f8c75d.png
5179
+ 2026-05-11 20:25:10,323 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001B7BDBC9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a028f86f42c431356712eb5, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028f86f42c431356712eb5"}, "remainingTimeMS": 30}
5180
+ 2026-05-11 20:26:39,620 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5181
+ 2026-05-11 20:27:01,355 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5182
+ 2026-05-11 20:27:01,729 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\032d76c129a442cebeac3323bc4fc080.png
5183
+ 2026-05-11 20:27:01,908 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001B7BDBC9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a028ff5f42c431356712eb6, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a028ff5f42c431356712eb6"}, "remainingTimeMS": 30}
5184
+ 2026-05-11 20:27:10,935 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5185
+ 2026-05-11 20:27:31,741 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5186
+ 2026-05-11 20:27:32,280 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\08a1798e9521455a902edda85dc029cc.png
5187
+ 2026-05-11 20:27:32,545 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001B7BDBC9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a029014f42c431356712eb7, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a029014f42c431356712eb7"}, "remainingTimeMS": 30}
5188
+ 2026-05-11 20:27:58,182 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5189
+ 2026-05-11 20:28:46,217 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5190
+ 2026-05-11 20:28:46,940 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\1bc4caa711694fb8acca6b23159f754f.png
5191
+ 2026-05-11 20:28:47,112 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001B7BDBC9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a02905ff42c431356712eb8, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02905ff42c431356712eb8"}, "remainingTimeMS": 30}
5192
+ 2026-05-11 20:29:03,508 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5193
+ 2026-05-11 20:29:44,213 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5194
+ 2026-05-11 20:29:44,750 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\2a5b913b68a243379027cd01b187b4cd.png
5195
+ 2026-05-11 20:29:44,955 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001B7BDBC9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a029098f42c431356712eb9, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a029098f42c431356712eb9"}, "remainingTimeMS": 30}
5196
+ 2026-05-11 20:29:53,559 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5197
+ 2026-05-11 20:30:37,983 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5198
+ 2026-05-11 20:30:38,490 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\836b20819d49430a9fc94c02e8e07885.png
5199
+ 2026-05-11 20:30:38,711 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001B7BDBC9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a0290cef42c431356712eba, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a0290cef42c431356712eba"}, "remainingTimeMS": 30}
5200
+ 2026-05-11 21:01:35,945 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a02980ff2c1d98dfeb7f208, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02980ff2c1d98dfeb7f208"}, "remainingTimeMS": 30}
5201
+ 2026-05-11 21:01:37,029 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "count", "topologyDescription": "<TopologyDescription id: 6a029811f2c1d98dfeb7f209, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a029811f2c1d98dfeb7f209"}, "remainingTimeMS": 30}
5202
+ 2026-05-11 21:01:40,034 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "Primary()", "operation": "find", "topologyDescription": "<TopologyDescription id: 6a029814f2c1d98dfeb7f20a, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a029814f2c1d98dfeb7f20a"}, "remainingTimeMS": 30}
5203
+ 2026-05-11 21:01:47,994 INFO backend.segmentation: /api/generate-image received file: hyper-reality-1778254222964.jpg (84903 bytes)
5204
+ 2026-05-11 21:02:33,393 INFO httpx: HTTP Request: POST https://api.openai.com/v1/images/edits "HTTP/1.1 200 OK"
5205
+ 2026-05-11 21:02:34,216 INFO backend.segmentation: Saved generated image to C:\Users\alane\OneDrive\Escritorio\Trabajo\Prueba-PoC\backend\uploads\generated\dbd083972cf5430998885a9f7c36ac0a.png
5206
+ 2026-05-11 21:02:34,417 INFO pymongo.serverSelection: {"message": "Waiting for suitable server to become available", "selector": "<function writable_server_selector at 0x000001F9459E9D00>", "operation": "update", "topologyDescription": "<TopologyDescription id: 6a02984af2c1d98dfeb7f20b, topology_type: ReplicaSetNoPrimary, servers: [<ServerDescription ('ac-tynjizz-shard-00-00.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-01.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>, <ServerDescription ('ac-tynjizz-shard-00-02.n9htwoa.mongodb.net', 27017) server_type: Unknown, rtt: None>]>", "clientId": {"$oid": "6a02984af2c1d98dfeb7f20b"}, "remainingTimeMS": 30}
backend/main.py CHANGED
@@ -1,6 +1,7 @@
1
  import mimetypes
2
  import os
3
  import subprocess
 
4
  import threading
5
  import time
6
  from pathlib import Path
@@ -9,6 +10,7 @@ from dotenv import load_dotenv
9
  load_dotenv(Path(__file__).resolve().parent / ".env")
10
 
11
  from fastapi import FastAPI, Request
 
12
  from fastapi.middleware.cors import CORSMiddleware
13
  from fastapi.staticfiles import StaticFiles
14
 
@@ -45,6 +47,9 @@ async def remove_x_frame_options(request: Request, call_next):
45
  # Comentamos las demás inclusiones para deshabilitar funcionalidades
46
  # posteriores (segmentación, inpainting, sesiones, catálogo, etc.).
47
  # Re-activar routers necesarios para el frontend
 
 
 
48
  app.include_router(sessions.router)
49
  app.include_router(segmentation.router)
50
  app.include_router(openai_image.router)
@@ -60,8 +65,37 @@ UPLOADS_DIR.mkdir(parents=True, exist_ok=True)
60
  app.mount("/uploads", StaticFiles(directory=UPLOADS_DIR), name="uploads")
61
 
62
  if (FRONTEND_DIST / "index.html").exists():
63
- # Montado en "/" como catch-all para SPA los routers de API tienen prioridad
64
- app.mount("/", StaticFiles(directory=FRONTEND_DIST, html=True), name="frontend")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
 
67
  # Frontend watcher (development helper)
@@ -85,8 +119,23 @@ def scan_frontend_sources() -> dict:
85
  def run_frontend_build() -> None:
86
  if not FRONTEND_DIR.exists():
87
  return
 
 
 
 
 
 
 
 
 
 
88
  print("[backend] Ejecutando build del frontend...")
89
- result = subprocess.run(["npm", "run", "build"], cwd=str(FRONTEND_DIR), capture_output=True, text=True)
 
 
 
 
 
90
  if result.returncode != 0:
91
  print("[backend] Build falló:")
92
  print(result.stdout)
 
1
  import mimetypes
2
  import os
3
  import subprocess
4
+ import shutil
5
  import threading
6
  import time
7
  from pathlib import Path
 
10
  load_dotenv(Path(__file__).resolve().parent / ".env")
11
 
12
  from fastapi import FastAPI, Request
13
+ from fastapi.responses import RedirectResponse, FileResponse
14
  from fastapi.middleware.cors import CORSMiddleware
15
  from fastapi.staticfiles import StaticFiles
16
 
 
47
  # Comentamos las demás inclusiones para deshabilitar funcionalidades
48
  # posteriores (segmentación, inpainting, sesiones, catálogo, etc.).
49
  # Re-activar routers necesarios para el frontend
50
+ app.include_router(auth.router)
51
+ app.include_router(media.router)
52
+ app.include_router(pages.router)
53
  app.include_router(sessions.router)
54
  app.include_router(segmentation.router)
55
  app.include_router(openai_image.router)
 
65
  app.mount("/uploads", StaticFiles(directory=UPLOADS_DIR), name="uploads")
66
 
67
  if (FRONTEND_DIST / "index.html").exists():
68
+ # Montar la SPA únicamente en /app (el servidor tiene su propia landing en /)
69
+ app.mount("/app", StaticFiles(directory=FRONTEND_DIST, html=True), name="frontend_app")
70
+ # También exponer los assets estáticos en la raíz /assets para que los
71
+ # archivos generados por Vite (con rutas absolutas `/assets/...`) sean
72
+ # resueltos correctamente cuando la SPA se carga desde /app.
73
+ assets_dir = FRONTEND_DIST / "assets"
74
+ if assets_dir.exists():
75
+ app.mount("/assets", StaticFiles(directory=assets_dir), name="frontend_assets")
76
+ # Servir manifest y favicon desde el dist para PWA/links absolutos
77
+ manifest = FRONTEND_DIST / "manifest.json"
78
+ favicon = FRONTEND_DIST / "favicon.svg"
79
+ if manifest.exists():
80
+ @app.get("/manifest.json")
81
+ async def _manifest():
82
+ return FileResponse(str(manifest), media_type="application/manifest+json")
83
+ if favicon.exists():
84
+ @app.get("/favicon.svg")
85
+ async def _favicon():
86
+ return FileResponse(str(favicon), media_type="image/svg+xml")
87
+
88
+ # Ruta raíz: servir `backend/home.html` cuando exista (landing del servidor)
89
+ HOME_HTML = BASE_DIR / "home.html"
90
+ if HOME_HTML.exists():
91
+ @app.get("/")
92
+ async def _root_home():
93
+ return FileResponse(str(HOME_HTML), media_type="text/html")
94
+ else:
95
+ # Si no existe `home.html`, redirigimos a la SPA en /app
96
+ @app.get("/")
97
+ async def _root_redirect():
98
+ return RedirectResponse(url="/app")
99
 
100
 
101
  # Frontend watcher (development helper)
 
119
  def run_frontend_build() -> None:
120
  if not FRONTEND_DIR.exists():
121
  return
122
+ # Allow disabling automatic frontend builds (useful in production or CI without Node installed)
123
+ if os.getenv("SKIP_FRONTEND_BUILD", "").lower() in {"1", "true", "yes"}:
124
+ print("[backend] SKIP_FRONTEND_BUILD is set — skipping frontend build.")
125
+ return
126
+
127
+ npm_path = shutil.which("npm")
128
+ if not npm_path:
129
+ print("[backend] npm not found in PATH — skipping frontend build. Install Node.js/npm to enable auto-build or set SKIP_FRONTEND_BUILD=1 to disable this message.")
130
+ return
131
+
132
  print("[backend] Ejecutando build del frontend...")
133
+ try:
134
+ result = subprocess.run([npm_path, "run", "build"], cwd=str(FRONTEND_DIR), capture_output=True, text=True)
135
+ except FileNotFoundError:
136
+ print("[backend] Error: npm executable not found when attempting to run build. Skipping frontend build.")
137
+ return
138
+
139
  if result.returncode != 0:
140
  print("[backend] Build falló:")
141
  print(result.stdout)
backend/routers/__pycache__/auth.cpython-312.pyc CHANGED
Binary files a/backend/routers/__pycache__/auth.cpython-312.pyc and b/backend/routers/__pycache__/auth.cpython-312.pyc differ
 
backend/routers/__pycache__/openai_image.cpython-312.pyc CHANGED
Binary files a/backend/routers/__pycache__/openai_image.cpython-312.pyc and b/backend/routers/__pycache__/openai_image.cpython-312.pyc differ
 
backend/routers/auth.py CHANGED
@@ -21,7 +21,7 @@ def _get_col():
21
  global _client, _db, _col
22
  if _col is None:
23
  if not MONGODB_URI:
24
- raise RuntimeError("MONGODB_URI no configurado")
25
  _client = AsyncIOMotorClient(MONGODB_URI)
26
  _db = _client["hyper_reality"]
27
  _col = _db["clients"]
@@ -87,6 +87,14 @@ async def startup():
87
 
88
  @router.post("/api/token")
89
  async def token(client_id: str = Form(...)):
 
 
 
 
 
 
 
 
90
  col = _get_col()
91
  doc = await col.find_one({"_id": client_id})
92
  if not doc:
@@ -103,10 +111,13 @@ async def config(client_id: str = Query(None), token: str = Query(None)):
103
  return JSONResponse(content={"error": "token inválido o expirado"}, status_code=401)
104
  if not client_id:
105
  return JSONResponse(content={"error": "client_id o token requerido"}, status_code=400)
106
-
107
- col = _get_col()
108
- doc = await col.find_one({"_id": client_id})
109
- datos = doc or {"nombre": "Cliente Desconocido", "color_primario": "#f97316", "created_at": _now_iso()}
 
 
 
110
  datos.pop("_id", None)
111
  ACTIVE_SESSIONS[client_id] = _now_iso()
112
  return JSONResponse(content={"client_id": client_id, **datos})
@@ -126,6 +137,18 @@ async def session_start(client_id: str = Form(None), token: str = Form(None)):
126
 
127
  @router.get("/api/keys")
128
  async def api_keys():
 
 
 
 
 
 
 
 
 
 
 
 
129
  col = _get_col()
130
  docs = await col.find({}).to_list(length=500)
131
  keys = [
@@ -144,8 +167,11 @@ async def api_keys():
144
  async def generate_key(request: Request, nombre: str = Form(...), color_primario: str = Form("#8b5cf6")):
145
  new_key = f"CLIENTE_{uuid4().hex[:8].upper()}"
146
  doc = {"_id": new_key, "nombre": nombre, "color_primario": color_primario, "created_at": _now_iso()}
147
- col = _get_col()
148
- await col.insert_one(doc)
 
 
 
149
  base_url = str(request.base_url).rstrip("/")
150
  if request.headers.get("x-forwarded-proto") == "https" and base_url.startswith("http://"):
151
  base_url = "https://" + base_url[7:]
@@ -164,6 +190,13 @@ async def generate_key(request: Request, nombre: str = Form(...), color_primario
164
 
165
  @router.delete("/api/keys/{client_id}")
166
  async def delete_key(client_id: str):
 
 
 
 
 
 
 
167
  col = _get_col()
168
  result = await col.delete_one({"_id": client_id})
169
  if result.deleted_count == 0:
 
21
  global _client, _db, _col
22
  if _col is None:
23
  if not MONGODB_URI:
24
+ return None
25
  _client = AsyncIOMotorClient(MONGODB_URI)
26
  _db = _client["hyper_reality"]
27
  _col = _db["clients"]
 
87
 
88
  @router.post("/api/token")
89
  async def token(client_id: str = Form(...)):
90
+ if not MONGODB_URI:
91
+ # fallback in-memory lookup
92
+ doc = next((c for c in _DEFAULT_CLIENTS if c["_id"] == client_id), None)
93
+ if not doc:
94
+ return JSONResponse(content={"error": "client_id inválido"}, status_code=400)
95
+ tok = generate_token_for_client(client_id)
96
+ return JSONResponse(content={"token": tok, "expires_in": TOKEN_TTL})
97
+
98
  col = _get_col()
99
  doc = await col.find_one({"_id": client_id})
100
  if not doc:
 
111
  return JSONResponse(content={"error": "token inválido o expirado"}, status_code=401)
112
  if not client_id:
113
  return JSONResponse(content={"error": "client_id o token requerido"}, status_code=400)
114
+ if not MONGODB_URI:
115
+ doc = next((c for c in _DEFAULT_CLIENTS if c["_id"] == client_id), None)
116
+ datos = doc or {"nombre": "Cliente Desconocido", "color_primario": "#f97316", "created_at": _now_iso()}
117
+ else:
118
+ col = _get_col()
119
+ doc = await col.find_one({"_id": client_id})
120
+ datos = doc or {"nombre": "Cliente Desconocido", "color_primario": "#f97316", "created_at": _now_iso()}
121
  datos.pop("_id", None)
122
  ACTIVE_SESSIONS[client_id] = _now_iso()
123
  return JSONResponse(content={"client_id": client_id, **datos})
 
137
 
138
  @router.get("/api/keys")
139
  async def api_keys():
140
+ if not MONGODB_URI:
141
+ keys = [
142
+ {
143
+ "client_id": d["_id"],
144
+ "nombre": d.get("nombre", ""),
145
+ "color_primario": d.get("color_primario", ""),
146
+ "created_at": d.get("created_at", ""),
147
+ }
148
+ for d in _DEFAULT_CLIENTS
149
+ ]
150
+ return JSONResponse(content={"keys": keys, "count": len(keys)})
151
+
152
  col = _get_col()
153
  docs = await col.find({}).to_list(length=500)
154
  keys = [
 
167
  async def generate_key(request: Request, nombre: str = Form(...), color_primario: str = Form("#8b5cf6")):
168
  new_key = f"CLIENTE_{uuid4().hex[:8].upper()}"
169
  doc = {"_id": new_key, "nombre": nombre, "color_primario": color_primario, "created_at": _now_iso()}
170
+ if not MONGODB_URI:
171
+ _DEFAULT_CLIENTS.append(doc)
172
+ else:
173
+ col = _get_col()
174
+ await col.insert_one(doc)
175
  base_url = str(request.base_url).rstrip("/")
176
  if request.headers.get("x-forwarded-proto") == "https" and base_url.startswith("http://"):
177
  base_url = "https://" + base_url[7:]
 
190
 
191
  @router.delete("/api/keys/{client_id}")
192
  async def delete_key(client_id: str):
193
+ if not MONGODB_URI:
194
+ before = len(_DEFAULT_CLIENTS)
195
+ _DEFAULT_CLIENTS[:] = [c for c in _DEFAULT_CLIENTS if c["_id"] != client_id]
196
+ if len(_DEFAULT_CLIENTS) == before:
197
+ return JSONResponse(content={"error": "client_id no encontrado"}, status_code=404)
198
+ return JSONResponse(content={"status": "eliminado", "client_id": client_id})
199
+
200
  col = _get_col()
201
  result = await col.delete_one({"_id": client_id})
202
  if result.deleted_count == 0:
backend/routers/openai_image.py CHANGED
@@ -1,32 +1,142 @@
1
  from fastapi import APIRouter, File, Form, HTTPException, UploadFile
 
2
  from fastapi.responses import JSONResponse
3
  from PIL import Image
4
  import io
 
 
 
 
5
 
6
  from services.openai_service import generate_image_with_openai
 
7
 
8
  router = APIRouter()
9
 
 
 
 
 
 
 
10
 
11
  @router.post("/api/generate-image")
12
  async def generate_image_endpoint(
13
- file: UploadFile = File(...),
 
 
14
  texture: str = Form(...),
15
  api_key: str | None = Form(None),
16
  preserve: int | None = Form(0),
 
17
  ):
18
- if not file.content_type or not file.content_type.startswith("image/"):
19
- raise HTTPException(status_code=400, detail="El archivo debe ser una imagen")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  try:
22
- contents = await file.read()
23
  pil = Image.open(io.BytesIO(contents)).convert("RGB")
24
  except Exception:
25
  raise HTTPException(status_code=400, detail="No se pudo leer la imagen subida")
26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  png_bytes, msg = generate_image_with_openai(api_key, pil, texture, preserve)
28
  if png_bytes is None:
29
  raise HTTPException(status_code=500, detail=msg)
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  b64 = __import__("base64").b64encode(png_bytes).decode("ascii")
32
  return JSONResponse(content={"result_b64": b64, "message": msg})
 
1
  from fastapi import APIRouter, File, Form, HTTPException, UploadFile
2
+ from pathlib import Path
3
  from fastapi.responses import JSONResponse
4
  from PIL import Image
5
  import io
6
+ import base64
7
+ import os
8
+ from uuid import uuid4
9
+ from motor.motor_asyncio import AsyncIOMotorClient
10
 
11
  from services.openai_service import generate_image_with_openai
12
+ from core.config import logger
13
 
14
  router = APIRouter()
15
 
16
+ BASE_DIR = Path(__file__).resolve().parent.parent
17
+ UPLOADS_DIR = BASE_DIR / "uploads"
18
+ GENERATED_DIR = UPLOADS_DIR / "generated"
19
+ GENERATED_DIR.mkdir(parents=True, exist_ok=True)
20
+ MONGODB_URI = os.getenv("MONGODB_URI", "")
21
+
22
 
23
  @router.post("/api/generate-image")
24
  async def generate_image_endpoint(
25
+ file: UploadFile | None = File(None),
26
+ image_b64: str | None = Form(None),
27
+ source_filename: str | None = Form(None),
28
  texture: str = Form(...),
29
  api_key: str | None = Form(None),
30
  preserve: int | None = Form(0),
31
+ user_id: str | None = Form(None),
32
  ):
33
+ # Accept either a multipart file upload (`file`), a server-side filename (`source_filename`),
34
+ # or a base64 string in `image_b64`.
35
+ contents: bytes | None = None
36
+ # If a source_filename was provided, try to read it from the uploads dir and prefer it.
37
+ if source_filename:
38
+ # Accept either bare filename or path containing '/uploads/<filename>'
39
+ name = source_filename.split("/uploads/")[-1]
40
+ candidate = UPLOADS_DIR / name
41
+ if candidate.exists():
42
+ try:
43
+ contents = candidate.read_bytes()
44
+ logger.info("/api/generate-image using server upload file: %s", str(candidate))
45
+ except Exception:
46
+ raise HTTPException(status_code=400, detail="No se pudo leer el archivo fuente en el servidor")
47
+ else:
48
+ raise HTTPException(status_code=404, detail="Archivo fuente no encontrado en el servidor")
49
+
50
+ # If user_id + source_filename provided, check cache in MongoDB for existing generated image
51
+ if MONGODB_URI and user_id and source_filename:
52
+ try:
53
+ client = AsyncIOMotorClient(MONGODB_URI)
54
+ db = client["hyper_reality"]
55
+ col = db["generated"]
56
+ doc = await col.find_one({"user_id": user_id, "original_filename": source_filename, "texture": texture})
57
+ client.close()
58
+ if doc and doc.get("generated_filename"):
59
+ gen_file = GENERATED_DIR / doc["generated_filename"]
60
+ if gen_file.exists():
61
+ logger.info("/api/generate-image returning cached generated image: %s", str(gen_file))
62
+ data = gen_file.read_bytes()
63
+ b64 = base64.b64encode(data).decode("ascii")
64
+ return JSONResponse(content={"result_b64": b64, "message": "Returned cached generated image"})
65
+ except Exception:
66
+ logger.exception("Error checking generated cache in DB")
67
+
68
+ # Accept either a multipart file upload (`file`) or a base64 string in `image_b64`.
69
+ if contents is None and file is not None and getattr(file, "filename", None):
70
+ # multipart upload
71
+ if not file.content_type or not file.content_type.startswith("image/"):
72
+ raise HTTPException(status_code=400, detail="El archivo debe ser una imagen")
73
+ try:
74
+ contents = await file.read()
75
+ logger.info("/api/generate-image received file: %s (%s bytes)", file.filename, len(contents))
76
+ except Exception:
77
+ raise HTTPException(status_code=400, detail="No se pudo leer la imagen subida")
78
+ elif contents is None and image_b64:
79
+ # form field may include data URL prefix; strip if present
80
+ try:
81
+ if image_b64.startswith("data:") and "," in image_b64:
82
+ image_b64 = image_b64.split(",", 1)[1]
83
+ contents = base64.b64decode(image_b64)
84
+ logger.info("/api/generate-image received image_b64 (%s bytes)", len(contents))
85
+ except Exception:
86
+ raise HTTPException(status_code=400, detail="El campo image_b64 no contiene base64 válido")
87
+ else:
88
+ raise HTTPException(status_code=400, detail="No image provided. Send multipart 'file' or include 'image_b64' form field.")
89
 
90
  try:
 
91
  pil = Image.open(io.BytesIO(contents)).convert("RGB")
92
  except Exception:
93
  raise HTTPException(status_code=400, detail="No se pudo leer la imagen subida")
94
 
95
+ # Basic sanity checks: reject empty or trivially-small images before calling OpenAI
96
+ if not contents or len(contents) < 256:
97
+ raise HTTPException(status_code=400, detail="Imagen vacía o demasiado pequeña")
98
+
99
+ try:
100
+ w, h = pil.size
101
+ if w < 32 or h < 32:
102
+ raise HTTPException(status_code=400, detail="Imagen demasiado pequeña para procesar")
103
+ except HTTPException:
104
+ raise
105
+ except Exception:
106
+ raise HTTPException(status_code=400, detail="No se pudo determinar el tamaño de la imagen")
107
+
108
  png_bytes, msg = generate_image_with_openai(api_key, pil, texture, preserve)
109
  if png_bytes is None:
110
  raise HTTPException(status_code=500, detail=msg)
111
 
112
+ # Persist generated image and record in DB (if available)
113
+ try:
114
+ gen_name = f"{uuid4().hex}.png"
115
+ gen_path = GENERATED_DIR / gen_name
116
+ gen_path.write_bytes(png_bytes)
117
+ logger.info("Saved generated image to %s", str(gen_path))
118
+ if MONGODB_URI and user_id:
119
+ try:
120
+ client = AsyncIOMotorClient(MONGODB_URI)
121
+ db = client["hyper_reality"]
122
+ col = db["generated"]
123
+ await col.replace_one(
124
+ {"user_id": user_id, "original_filename": source_filename or (getattr(file, 'filename', None) if file else None), "texture": texture},
125
+ {
126
+ "user_id": user_id,
127
+ "original_filename": source_filename or (getattr(file, 'filename', None) if file else None),
128
+ "texture": texture,
129
+ "generated_filename": gen_name,
130
+ "generated_url": f"/uploads/generated/{gen_name}",
131
+ "created_at": __import__('datetime').datetime.utcnow().isoformat() + 'Z',
132
+ },
133
+ upsert=True,
134
+ )
135
+ client.close()
136
+ except Exception:
137
+ logger.exception("Failed saving generated record to DB")
138
+ except Exception:
139
+ logger.exception("Failed saving generated image to disk")
140
+
141
  b64 = __import__("base64").b64encode(png_bytes).decode("ascii")
142
  return JSONResponse(content={"result_b64": b64, "message": msg})
backend/services/__pycache__/openai_service.cpython-312.pyc CHANGED
Binary files a/backend/services/__pycache__/openai_service.cpython-312.pyc and b/backend/services/__pycache__/openai_service.cpython-312.pyc differ
 
backend/services/openai_service.py CHANGED
@@ -6,6 +6,8 @@ from typing import Any, Tuple
6
 
7
  import openai
8
  from PIL import Image
 
 
9
 
10
  from core.config import TEXTURE_DIR
11
 
@@ -74,7 +76,6 @@ def generate_image_with_openai(
74
  pil_img: Image.Image,
75
  texture: str,
76
  preserve_percent: int = 0,
77
- use_vision: bool = False,
78
  ) -> Tuple[bytes | None, str]:
79
  """
80
  Genera/edita una imagen usando la API de OpenAI (gpt-image-1 edit).
@@ -85,38 +86,35 @@ def generate_image_with_openai(
85
  return None, "OpenAI API key not provided"
86
 
87
  texture_path, texture_display = _resolve_texture_path(texture)
88
- if not texture_path:
89
- # intentar usar descripción simple
90
- texture_desc = TEXTURE_DESCRIPTIONS.get(texture, texture)
91
- else:
92
- texture_desc = Path(texture_path).stem
93
 
94
- # Build specialized specs for ACM / WPC / DECK
95
  acm_specs = ""
96
- if texture_display and texture_display.upper().startswith("ACM"):
97
  acm_specs = (
98
  "ACM aluminum composite panel 4mm thick, 0.40mm aluminum layers, "
99
  "panels sized 1.22m x 2.44m, clean precision-cut joints between panels, "
100
  )
101
 
 
102
  deck_specs = ""
103
- if Path(texture).stem.startswith("DECK") or ("DECK" in Path(texture).stem):
104
  deck_specs = (
105
  "WPC deck boards (ash / \"ceniza\" color), plank dimensions 2.5 cm thick x 14 cm wide x 220 cm long, "
106
  "installed as horizontal planks with 3-5 mm spacing and hidden fasteners, realistic wood grain, slight bevel on plank edges."
107
  )
108
 
 
109
  wpc_panel_specs = ""
110
- if ("WPC" in Path(texture).stem) and ("DECK" not in Path(texture).stem):
111
  wpc_panel_specs = (
112
  "WPC wall panels, each panel sized 0.16 m x 2.90 m (16 cm x 290 cm), realistic ash tone when appropriate, "
113
  "installed with narrow vertical joints matching panel width and clean aligned seams."
114
  )
115
 
116
- # If we have a texture file, attempt to compute its representative color and ask model to match it
117
- tex_hex = _get_texture_hex(texture)
118
-
119
- # Build prompt depending on type
120
  if deck_specs:
121
  prompt = (
122
  f"Edit ONLY the HORIZONTAL floor/deck surfaces (decks, terraces, balcony floors, wooden walkways). "
@@ -142,6 +140,31 @@ def generate_image_with_openai(
142
  f"Only change the wall surface material. Result must look like a photorealistic architectural photo."
143
  )
144
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  if tex_hex:
146
  prompt += f" Use the exact color sample {tex_hex} from the reference texture and match its hue, saturation and brightness precisely. Do not alter the color tone."
147
 
@@ -152,77 +175,30 @@ def generate_image_with_openai(
152
  orig_size = pil_img_orig.size
153
  square_img, padding, resized_dims = prepare_image_square(pil_img_orig)
154
 
 
 
 
 
 
 
 
 
 
 
 
155
  buf = io.BytesIO()
156
- square_img.save(buf, format="PNG")
157
  buf.seek(0)
158
-
159
- # Optionally run vision analysis to enrich prompt
160
- house_desc = ""
161
- if use_vision:
162
- try:
163
- bb = io.BytesIO()
164
- pil_img_orig.convert("RGB").save(bb, format="JPEG", quality=85)
165
- bb.seek(0)
166
- b64 = base64.b64encode(bb.getvalue()).decode("ascii")
167
-
168
- vision_prompt = (
169
- "You are an architectural photographer. Describe exactly what is visible in this house photo: "
170
- "number of floors, roof shape, main materials, prominent features (garage, balconies, large windows), "
171
- "lighting conditions, and anything that affects how to apply a facade material. "
172
- "Return a concise English description (max 200 words)."
173
- )
174
-
175
- vision_resp = client.chat.completions.create(
176
- model="gpt-4o-mini",
177
- messages=[{"role": "user", "content": f"data:image/jpeg;base64,{b64}\n\n{vision_prompt}"}],
178
- max_tokens=300,
179
- )
180
-
181
- try:
182
- house_desc = vision_resp.choices[0].message.content.strip()
183
- except Exception:
184
- house_desc = str(vision_resp)
185
- except Exception:
186
- house_desc = ""
187
-
188
- if house_desc:
189
- prompt = f"House description: {house_desc}. " + prompt
190
-
191
- # If a texture file exists, try to attach it as a reference image to the API call.
192
- tex_path, _ = _resolve_texture_path(texture)
193
- try:
194
- if tex_path:
195
- with open(tex_path, "rb") as tf:
196
- tex_buf = io.BytesIO(tf.read())
197
- tex_buf.seek(0)
198
- response = client.images.edit(
199
- model="gpt-image-1",
200
- image=("image.png", buf, "image/png"),
201
- reference_image=(Path(tex_path).name, tex_buf, "image/png"),
202
- prompt=prompt,
203
- n=1,
204
- size="1024x1024",
205
- quality="medium",
206
- )
207
- else:
208
- response = client.images.edit(
209
- model="gpt-image-1",
210
- image=("image.png", buf, "image/png"),
211
- prompt=prompt,
212
- n=1,
213
- size="1024x1024",
214
- quality="medium",
215
- )
216
- except Exception:
217
- # Fallback: try without reference_image if SDK rejected the param
218
- response = client.images.edit(
219
- model="gpt-image-1",
220
- image=("image.png", buf, "image/png"),
221
- prompt=prompt,
222
- n=1,
223
- size="1024x1024",
224
- quality="medium",
225
- )
226
 
227
  img_data = base64.b64decode(response.data[0].b64_json)
228
  result_square = Image.open(io.BytesIO(img_data)).convert("RGBA")
@@ -231,20 +207,24 @@ def generate_image_with_openai(
231
  result_cropped = result_square.crop((left, top, right, bottom))
232
  result = result_cropped.resize(orig_size, Image.LANCZOS).convert("RGB")
233
 
234
- # Apply optional blending/preserve percent (0-100)
235
- try:
236
- preserve = max(0.0, min(1.0, float(preserve_percent) / 100.0))
237
- except Exception:
238
- preserve = 0.0
239
-
240
- if preserve > 0:
241
- pil_img_rgb = pil_img_orig.convert("RGB")
242
- final = Image.blend(result, pil_img_rgb, preserve)
243
- else:
244
- final = result
245
 
246
  out_buf = io.BytesIO()
247
  final.save(out_buf, format="PNG")
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  return out_buf.getvalue(), f"Generated with texture {texture_display if texture_display else texture_desc}"
249
 
250
  except Exception as exc:
 
6
 
7
  import openai
8
  from PIL import Image
9
+ import time
10
+ from uuid import uuid4
11
 
12
  from core.config import TEXTURE_DIR
13
 
 
76
  pil_img: Image.Image,
77
  texture: str,
78
  preserve_percent: int = 0,
 
79
  ) -> Tuple[bytes | None, str]:
80
  """
81
  Genera/edita una imagen usando la API de OpenAI (gpt-image-1 edit).
 
86
  return None, "OpenAI API key not provided"
87
 
88
  texture_path, texture_display = _resolve_texture_path(texture)
89
+ # derive texture_stem for rules
90
+ texture_stem = Path(texture_path).stem if texture_path else (Path(texture).stem if texture and isinstance(texture, str) else "")
91
+ texture_desc = TEXTURE_DESCRIPTIONS.get(texture_stem, texture_display if texture_display else texture)
 
 
92
 
93
+ # Specs técnicas ACM
94
  acm_specs = ""
95
+ if texture_stem and texture_stem.upper().startswith("ACM"):
96
  acm_specs = (
97
  "ACM aluminum composite panel 4mm thick, 0.40mm aluminum layers, "
98
  "panels sized 1.22m x 2.44m, clean precision-cut joints between panels, "
99
  )
100
 
101
+ # Enriquecimientos específicos para DECK (pisos / decking)
102
  deck_specs = ""
103
+ if texture_stem.startswith("DECK") or ("DECK" in texture_stem):
104
  deck_specs = (
105
  "WPC deck boards (ash / \"ceniza\" color), plank dimensions 2.5 cm thick x 14 cm wide x 220 cm long, "
106
  "installed as horizontal planks with 3-5 mm spacing and hidden fasteners, realistic wood grain, slight bevel on plank edges."
107
  )
108
 
109
+ # Especificación de PANEL WPC (revestimiento exterior/interior)
110
  wpc_panel_specs = ""
111
+ if ("WPC" in texture_stem) and ("DECK" not in texture_stem):
112
  wpc_panel_specs = (
113
  "WPC wall panels, each panel sized 0.16 m x 2.90 m (16 cm x 290 cm), realistic ash tone when appropriate, "
114
  "installed with narrow vertical joints matching panel width and clean aligned seams."
115
  )
116
 
117
+ # Build prompt variants
 
 
 
118
  if deck_specs:
119
  prompt = (
120
  f"Edit ONLY the HORIZONTAL floor/deck surfaces (decks, terraces, balcony floors, wooden walkways). "
 
140
  f"Only change the wall surface material. Result must look like a photorealistic architectural photo."
141
  )
142
 
143
+ # Añadir instrucciones de preservación de escala/dimensiones
144
+ size_text = ""
145
+ if deck_specs:
146
+ size_text = (
147
+ " Respect the real-world plank dimensions: 2.5 cm thick x 14 cm wide x 220 cm long. "
148
+ "Scale the texture so plank width and length match these measurements proportionally in the photo, including spacing."
149
+ )
150
+ elif wpc_panel_specs:
151
+ size_text = (
152
+ " Respect the real-world panel dimensions: 0.16 m x 2.90 m per panel. "
153
+ "Scale the texture so vertical panel widths and joint spacing correspond to these real dimensions."
154
+ )
155
+
156
+ constraint_text = (
157
+ " CRITICAL: Do NOT change geometry, add or remove architectural elements, alter windows, doors, roofs, "
158
+ "gardens, people, vehicles, or sky. Do NOT modify shadows, reflections, perspective, or lighting. "
159
+ "Only replace the surface material of the targeted areas. Preserve all original non-target textures, finishes, and details. "
160
+ "Match the scale, grain and color of the selected texture; do not stylize or apply artistic filters."
161
+ ) + size_text
162
+
163
+ texture_ref_text = f" Use the selected texture sample as the reference for color, pattern and grain: {texture_desc}."
164
+ prompt = prompt + constraint_text + texture_ref_text
165
+
166
+ # If we have a texture file, attempt to compute its representative color and ask model to match it
167
+ tex_hex = _get_texture_hex(texture)
168
  if tex_hex:
169
  prompt += f" Use the exact color sample {tex_hex} from the reference texture and match its hue, saturation and brightness precisely. Do not alter the color tone."
170
 
 
175
  orig_size = pil_img_orig.size
176
  square_img, padding, resized_dims = prepare_image_square(pil_img_orig)
177
 
178
+ # Ensure we send an RGB image with white background (match imagneConaI/app.py behavior)
179
+ if square_img.mode != "RGB":
180
+ bg = Image.new("RGB", square_img.size, (255, 255, 255))
181
+ if square_img.mode == "RGBA":
182
+ bg.paste(square_img, (0, 0), square_img)
183
+ else:
184
+ bg.paste(square_img.convert("RGB"))
185
+ square_for_api = bg
186
+ else:
187
+ square_for_api = square_img
188
+
189
  buf = io.BytesIO()
190
+ square_for_api.save(buf, format="PNG")
191
  buf.seek(0)
192
+ img_input_bytes = buf.getvalue()
193
+
194
+ # Call the API with the same parameters as imagneConaI/app.py (no reference_image param)
195
+ response = client.images.edit(
196
+ model="gpt-image-1",
197
+ image=("image.png", io.BytesIO(img_input_bytes), "image/png"),
198
+ prompt=prompt,
199
+ n=1,
200
+ size="1024x1024",
201
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
  img_data = base64.b64decode(response.data[0].b64_json)
204
  result_square = Image.open(io.BytesIO(img_data)).convert("RGBA")
 
207
  result_cropped = result_square.crop((left, top, right, bottom))
208
  result = result_cropped.resize(orig_size, Image.LANCZOS).convert("RGB")
209
 
210
+ # Do not blend with original image: return the generated result as-is
211
+ final = result
 
 
 
 
 
 
 
 
 
212
 
213
  out_buf = io.BytesIO()
214
  final.save(out_buf, format="PNG")
215
+
216
+ # Debug: save prompt, input and response when requested
217
+ try:
218
+ if os.getenv("OPENAI_SAVE_RESPONSES", "0") == "1":
219
+ dbg_dir = Path.cwd() / "backend_outputs" / "openai_debug"
220
+ dbg_dir.mkdir(parents=True, exist_ok=True)
221
+ prefix = f"openai_{int(time.time())}_{uuid4().hex[:6]}"
222
+ (dbg_dir / f"{prefix}_prompt.txt").write_text(prompt, encoding="utf8")
223
+ (dbg_dir / f"{prefix}_input.png").write_bytes(img_input_bytes)
224
+ (dbg_dir / f"{prefix}_resp.png").write_bytes(out_buf.getvalue())
225
+ except Exception:
226
+ pass
227
+
228
  return out_buf.getvalue(), f"Generated with texture {texture_display if texture_display else texture_desc}"
229
 
230
  except Exception as exc:
backend/uploads/0489e41ca42c4f62972f9df4048f4fee.jpg ADDED
backend/uploads/0d38e64424a44bfea0d2cd60b6ab7ca7.jpg ADDED
backend/uploads/44551e85423f44e28220e796e8aad972.jpg ADDED
backend/uploads/6aae686070084421be72844fcab3432c.jpg ADDED
backend/uploads/91484306cbd94cc187cf2666993e17fd.jpg ADDED
backend/uploads/977ec30d99304e2a95328c448b8e71f7.jpg ADDED
backend/uploads/99256e3e599c4ed0ab1cfb9ed3823137.jpg ADDED
backend/uploads/b60b62ea9b67460d9f192fc7e46f0017.jpg ADDED
backend/uploads/db11756ac9d04460a06785f066342331.jpg ADDED
backend/uploads/f652de6149aa4fd6a775c770e5736367.jpg ADDED
backend/uploads/f8a0db84234c426e9252ee65e958f77a.jpg ADDED
backend/uploads/generated/032d76c129a442cebeac3323bc4fc080.png ADDED

Git LFS Details

  • SHA256: fde25f2ba7ad8369b6817107d31fe77a5fd9347d89ca7c57bde4c06b97584c74
  • Pointer size: 131 Bytes
  • Size of remote file: 945 kB
backend/uploads/generated/08a1798e9521455a902edda85dc029cc.png ADDED

Git LFS Details

  • SHA256: 3cbea9e8c91bbb618295e56203793479391566706d843ee9111d0ce9ba0d0005
  • Pointer size: 131 Bytes
  • Size of remote file: 961 kB
backend/uploads/generated/1bc4caa711694fb8acca6b23159f754f.png ADDED

Git LFS Details

  • SHA256: 7f9c3cabad3c73238f56dd30d4edc7d01812d488cade3903196211af0802ec0c
  • Pointer size: 132 Bytes
  • Size of remote file: 1.01 MB
backend/uploads/generated/231900b690ad4adb9883a3d600716a7d.png ADDED

Git LFS Details

  • SHA256: c32cef100b9059af9b482c82931c4a625f706331ca8abc37ebc7a864fad52448
  • Pointer size: 131 Bytes
  • Size of remote file: 819 kB
backend/uploads/generated/2a5b913b68a243379027cd01b187b4cd.png ADDED

Git LFS Details

  • SHA256: 1fdff8c1a287c15bdb26078698b1af879ba968c7da4347231d0b78dda5b9903d
  • Pointer size: 132 Bytes
  • Size of remote file: 1.04 MB
backend/uploads/generated/2d6e02255c3a4f00804ace65c49f19aa.png ADDED

Git LFS Details

  • SHA256: 9d40f2ac9f0ecc599893342ffec43496b157097f43d3fba9ae5e784cf539f571
  • Pointer size: 131 Bytes
  • Size of remote file: 840 kB
backend/uploads/generated/5b6f11b554f74c5486ce33e390f8c75d.png ADDED

Git LFS Details

  • SHA256: e99a34d522bde85e379f4bb5bcadd00ca4467034a66f9b4c141d3306c56b4051
  • Pointer size: 131 Bytes
  • Size of remote file: 864 kB
backend/uploads/generated/836b20819d49430a9fc94c02e8e07885.png ADDED

Git LFS Details

  • SHA256: c276f3e2337069481550396496f41ef08e64ba2ec7a8416c2d8ced8913697fac
  • Pointer size: 132 Bytes
  • Size of remote file: 1.14 MB
backend/uploads/generated/d664c83e3b614760834cdaad28769340.png ADDED

Git LFS Details

  • SHA256: 31e4e20f3bfb48cd5fac563e1d023a4b2fec8900efe40300944f19e9049943b4
  • Pointer size: 131 Bytes
  • Size of remote file: 822 kB
backend/uploads/generated/dbd083972cf5430998885a9f7c36ac0a.png ADDED

Git LFS Details

  • SHA256: 7e3d8ac8ca0f53912f22a4dc0b0a562eca79fd857707b216973797e6dc885be8
  • Pointer size: 131 Bytes
  • Size of remote file: 987 kB
frontend/dist/assets/index-CenEQfA2.css ADDED
@@ -0,0 +1 @@
 
 
1
+ *,:before,:after,::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border:0 solid #e5e7eb}:before,:after{--tw-content:""}html,:host{-webkit-text-size-adjust:100%;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{line-height:inherit;margin:0}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-feature-settings:normal;font-variation-settings:normal;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-feature-settings:inherit;font-variation-settings:inherit;font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:#0000;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{margin:0;padding:0;list-style:none}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder{opacity:1;color:#9ca3af}textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (width>=640px){.container{max-width:640px}}@media (width>=768px){.container{max-width:768px}}@media (width>=1024px){.container{max-width:1024px}}@media (width>=1280px){.container{max-width:1280px}}@media (width>=1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.inset-x-0{left:0;right:0}.-bottom-1{bottom:-.25rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.bottom-10{bottom:2.5rem}.bottom-12{bottom:3rem}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:.75rem}.right-0{right:0}.right-2{right:.5rem}.right-3{right:.75rem}.top-0{top:0}.top-1\/2{top:50%}.top-12{top:3rem}.top-14{top:3.5rem}.top-2{top:.5rem}.z-10{z-index:10}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.mb-1{margin-bottom:.25rem}.mb-10{margin-bottom:2.5rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-8{margin-top:2rem}.mt-\[3px\]{margin-top:3px}.block{display:block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.aspect-\[4\/3\]{aspect-ratio:4/3}.aspect-square{aspect-ratio:1}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-\[1px\]{height:1px}.h-full{height:100%}.h-screen{height:100vh}.max-h-full{max-height:100%}.min-h-0{min-height:0}.min-h-\[220px\]{min-height:220px}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-14{width:3.5rem}.w-2{width:.5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-\[80\%\]{width:80%}.w-fit{width:fit-content}.w-full{width:100%}.min-w-0{min-width:0}.max-w-7xl{max-width:80rem}.max-w-\[260px\]{max-width:260px}.max-w-full{max-width:100%}.max-w-xs{max-width:20rem}.flex-1{flex:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:-50%;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate:180deg;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-45{--tw-rotate:45deg;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}.animate-bounce{animation:1s infinite bounce}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:2s cubic-bezier(.4,0,.6,1) infinite pulse}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:1s linear infinite spin}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-5{gap:1.25rem}.gap-6{gap:1.5rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.whitespace-nowrap{white-space:nowrap}.rounded-2xl{border-radius:1rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.rounded-xl{border-radius:.75rem}.rounded-b-md{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-\[\#0047AB\]{--tw-border-opacity:1;border-color:rgb(0 71 171/var(--tw-border-opacity,1))}.border-\[\#0047AB\]\/10{border-color:#0047ab1a}.border-\[\#0047AB\]\/20{border-color:#0047ab33}.border-\[\#dbe7ff\]{--tw-border-opacity:1;border-color:rgb(219 231 255/var(--tw-border-opacity,1))}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-transparent{border-color:#0000}.bg-\[\#0047AB\]{--tw-bg-opacity:1;background-color:rgb(0 71 171/var(--tw-bg-opacity,1))}.bg-\[\#0047AB\]\/20{background-color:#0047ab33}.bg-\[\#0047AB\]\/80{background-color:#0047abcc}.bg-\[\#333333\]{--tw-bg-opacity:1;background-color:rgb(51 51 51/var(--tw-bg-opacity,1))}.bg-\[\#eaf1ff\]{--tw-bg-opacity:1;background-color:rgb(234 241 255/var(--tw-bg-opacity,1))}.bg-\[\#edf4ff\]{--tw-bg-opacity:1;background-color:rgb(237 244 255/var(--tw-bg-opacity,1))}.bg-\[\#f4f8ff\]{--tw-bg-opacity:1;background-color:rgb(244 248 255/var(--tw-bg-opacity,1))}.bg-black\/0{background-color:#0000}.bg-black\/40{background-color:#0006}.bg-black\/50{background-color:#00000080}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-950{--tw-bg-opacity:1;background-color:rgb(3 7 18/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-transparent{background-color:#0000}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/80{background-color:#fffc}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.bg-zinc-400{--tw-bg-opacity:1;background-color:rgb(161 161 170/var(--tw-bg-opacity,1))}.bg-gradient-to-t{background-image:linear-gradient(to top, var(--tw-gradient-stops))}.from-black\/60{--tw-gradient-from:#0009 var(--tw-gradient-from-position);--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.via-transparent{--tw-gradient-to:#0000 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from), transparent var(--tw-gradient-via-position), var(--tw-gradient-to)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-2{padding-bottom:.5rem}.pb-4{padding-bottom:1rem}.pl-11{padding-left:2.75rem}.pr-10{padding-right:2.5rem}.pr-12{padding-right:3rem}.text-left{text-align:left}.text-center{text-align:center}.font-sans{font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[13px\]{font-size:13px}.text-\[15px\]{font-size:15px}.text-\[16px\]{font-size:16px}.text-\[17px\]{font-size:17px}.text-\[9px\]{font-size:9px}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-light{font-weight:300}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.leading-tight{line-height:1.25}.tracking-tight{letter-spacing:-.025em}.tracking-tighter{letter-spacing:-.05em}.tracking-widest{letter-spacing:.1em}.text-\[\#0047AB\]{--tw-text-opacity:1;color:rgb(0 71 171/var(--tw-text-opacity,1))}.text-\[\#333333\],.text-\[\#333\]{--tw-text-opacity:1;color:rgb(51 51 51/var(--tw-text-opacity,1))}.text-\[\#4a7fd4\]{--tw-text-opacity:1;color:rgb(74 127 212/var(--tw-text-opacity,1))}.text-\[\#555\]{--tw-text-opacity:1;color:rgb(85 85 85/var(--tw-text-opacity,1))}.text-\[\#707070\]{--tw-text-opacity:1;color:rgb(112 112 112/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-white\/80{color:#fffc}.opacity-0{opacity:0}.opacity-100{opacity:1}.opacity-30{opacity:.3}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px #0000001a, 0 2px 4px -2px #0000001a;--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000), var(--tw-ring-shadow,0 0 #0000), var(--tw-shadow)}.shadow-\[\#0047AB\]\/20{--tw-shadow-color:#0047ab33;--tw-shadow:var(--tw-shadow-colored)}.outline{outline-style:solid}.blur{--tw-blur:blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px #0000000d);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter,backdrop-filter;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-property:all;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-property:opacity;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-duration:.15s;transition-timing-function:cubic-bezier(.4,0,.2,1)}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.\[animation-delay\:-0\.15s\]{animation-delay:-.15s}.\[animation-delay\:-0\.3s\]{animation-delay:-.3s}:root{--brand-blue:#0047ab;--brand-black:#333;--brand-gray:#707070;--brand-surface:#fff;--brand-light:#f4f8ff;--brand-border:#dbe7ff;--brand-contrast:#25d366;color:var(--brand-black);background:var(--brand-light);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:16px}*,:before,:after{box-sizing:border-box}html{overscroll-behavior:none;height:100%}body{color:#111827;overscroll-behavior:none;background:#f8fafc;height:100%;min-height:100dvh;margin:0}#root{flex-direction:column;height:100%;min-height:100dvh;display:flex}a{color:inherit;text-decoration:none}img{max-width:100%;display:block}h1,h2,h3,p{margin:0}ul{margin:0;padding:0;list-style:none}.last\:border-0:last-child{border-width:0}.hover\:border-\[\#0047AB\]:hover{--tw-border-opacity:1;border-color:rgb(0 71 171/var(--tw-border-opacity,1))}.hover\:border-\[\#0047AB\]\/40:hover{border-color:#0047ab66}.hover\:border-\[\#0047AB\]\/50:hover{border-color:#0047ab80}.hover\:bg-\[\#003a94\]:hover{--tw-bg-opacity:1;background-color:rgb(0 58 148/var(--tw-bg-opacity,1))}.hover\:bg-\[\#eaf1ff\]:hover{--tw-bg-opacity:1;background-color:rgb(234 241 255/var(--tw-bg-opacity,1))}.hover\:bg-\[\#eef4ff\]:hover{--tw-bg-opacity:1;background-color:rgb(238 244 255/var(--tw-bg-opacity,1))}.hover\:bg-\[\#f4f8ff\]:hover{--tw-bg-opacity:1;background-color:rgb(244 248 255/var(--tw-bg-opacity,1))}.hover\:bg-black:hover{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.hover\:bg-red-500:hover{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.hover\:text-\[\#002c75\]:hover{--tw-text-opacity:1;color:rgb(0 44 117/var(--tw-text-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.focus\:outline-none:focus{outline-offset:2px;outline:2px solid #0000}.disabled\:opacity-60:disabled{opacity:.6}.group:hover .group-hover\:translate-x-1{--tw-translate-x:.25rem;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:border-\[\#0047AB\]{--tw-border-opacity:1;border-color:rgb(0 71 171/var(--tw-border-opacity,1))}.group:hover .group-hover\:bg-black\/10{background-color:#0000001a}.group:hover .group-hover\:opacity-100{opacity:1}@media (width>=640px){.sm\:mb-14{margin-bottom:3.5rem}.sm\:mb-16{margin-bottom:4rem}.sm\:mb-4{margin-bottom:1rem}.sm\:mb-6{margin-bottom:1.5rem}.sm\:mb-8{margin-bottom:2rem}.sm\:mt-4{margin-top:1rem}.sm\:aspect-\[4\/3\]{aspect-ratio:4/3}.sm\:h-16{height:4rem}.sm\:h-32{height:8rem}.sm\:h-6{height:1.5rem}.sm\:h-8{height:2rem}.sm\:min-h-0{min-height:0}.sm\:w-16{width:4rem}.sm\:w-32{width:8rem}.sm\:w-6{width:1.5rem}.sm\:w-72{width:18rem}.sm\:w-8{width:2rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:gap-6{gap:1.5rem}.sm\:space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-4{padding-top:1rem;padding-bottom:1rem}.sm\:py-8{padding-top:2rem;padding-bottom:2rem}.sm\:text-2xl{font-size:1.5rem;line-height:2rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}.sm\:text-base{font-size:1rem;line-height:1.5rem}.sm\:text-lg{font-size:1.125rem;line-height:1.75rem}.sm\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (width>=768px){.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width>=1024px){.lg\:bottom-16{bottom:4rem}.lg\:top-16{top:4rem}.lg\:top-20{top:5rem}.lg\:mb-10{margin-bottom:2.5rem}.lg\:mb-20{margin-bottom:5rem}.lg\:mb-8{margin-bottom:2rem}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:gap-12{gap:3rem}.lg\:rounded-lg{border-radius:.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:text-4xl{font-size:2.25rem;line-height:2.5rem}}:root{color:var(--brand-black);background:var(--brand-light);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;line-height:1.5}*{box-sizing:border-box}html,body,#root{min-height:100%}body{background:var(--brand-light);color:var(--brand-black);margin:0}button,input,select,textarea{font:inherit}button{cursor:pointer}.app-shell{max-width:1200px;margin:0 auto;padding:24px}.topbar{background:var(--brand-surface);border:1px solid var(--brand-border);border-radius:18px;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:16px;padding:22px 24px;display:flex}.topbar h1{margin:0;font-size:clamp(1.9rem,2.5vw,2.6rem)}.topbar p{color:var(--brand-gray);margin:6px 0 0}.topbar-nav{flex-wrap:wrap;gap:10px;display:flex}.topbar-nav a{background:var(--brand-surface);color:var(--brand-black);border:1px solid #0000;border-radius:14px;justify-content:center;align-items:center;padding:10px 14px;text-decoration:none;transition:background .2s,border-color .2s;display:inline-flex}.topbar-nav a:hover{background:#fff;border-color:#d1d5db}.content{grid-template-columns:1.2fr 1.8fr;gap:24px;margin-top:24px;display:grid}.route-index,.panel,.viewer-panel,.bridge-panel,.room-setup-dropzone,.room-setup-card,.room-setup-preview{background:var(--brand-surface);border:1px solid var(--brand-border);border-radius:20px}.route-index,.panel,.viewer-panel,.bridge-panel,.room-setup-card{padding:22px}.route-index h2,.panel h2,.viewer-panel h2,.room-setup-copy h1,.room-setup-bottom h2{margin:0 0 16px}.route-index p,.room-setup-features li,.room-setup-drop-content p,.room-setup-drop-content small,.room-setup-card h3,.topbar p,.error-box,.empty-state{color:#475569}.route-index ul,.room-setup-features,.room-setup-filters,.room-setup-grid{margin:0;padding:0;list-style:none}.route-index ul{gap:12px;display:grid}.form-row{gap:14px;margin-bottom:20px;display:grid}label{color:var(--brand-black);font-size:.95rem}input,select,textarea{color:#111827;background:#fff;border:1px solid #d1d5db;border-radius:14px;width:100%;padding:14px 16px}input:focus,select:focus,textarea:focus{outline:2px solid var(--brand-blue);outline-offset:2px}.button,.button-primary,.button-secondary{border:1px solid #0000;border-radius:16px;justify-content:center;align-items:center;gap:10px;padding:14px 18px;font-weight:700;transition:all .2s;display:inline-flex}.button{background:var(--brand-black);color:var(--brand-surface)}.button:hover{opacity:.95}.button.secondary,.button-secondary{background:var(--brand-surface);color:var(--brand-black);border-color:var(--brand-border)}.error-box{color:#991b1b;background:#fef2f2;border:1px solid #fecaca;border-radius:16px;margin-top:16px;padding:16px}.empty-state{border:1px dashed #d1d5db;border-radius:18px;place-content:center;min-height:220px;display:grid}.room-setup{background:var(--brand-light)}.room-setup-inner{max-width:1200px;margin:0 auto;padding:32px 24px}.room-setup-header{justify-content:flex-end;margin-bottom:24px;display:flex}.room-setup-close{color:var(--brand-black);cursor:pointer;background:0 0;border:none;border-radius:999px;padding:10px}.room-setup-close:hover{background:var(--brand-border)}.room-setup-top{grid-template-columns:1.1fr .9fr;gap:24px;margin-bottom:40px;display:grid}.room-setup-copy h1{color:#111827;margin:0 0 24px;font-size:clamp(2rem,2.5vw,3rem)}.room-setup-features{gap:16px;display:grid}.room-setup-features li{color:#475569;align-items:center;gap:12px;display:flex}.room-setup-actions{gap:16px;display:grid}.button-primary{color:#fff;background:#111827}.button-primary:hover{opacity:.95}.button-secondary{color:#334155;background:#fff;border-color:#d1d5db}.button-secondary:hover{background:#f3f4f6}.button-icon-border{border:1px solid #d1d5db;border-radius:10px;justify-content:center;align-items:center;width:30px;height:30px;display:inline-flex}.room-setup-dropzone{border:1px dashed #d1d5db;border-radius:28px;justify-content:center;align-items:center;min-height:420px;padding:24px;transition:all .2s;display:flex;position:relative}.room-setup-dropzone.dragging{background:#f3f4f6}.room-setup-file-input{opacity:0;cursor:pointer;width:100%;height:100%;position:absolute;inset:0}.room-setup-drop-content{text-align:center}.room-setup-drop-icon{color:#64748b;background:#e2e8f0;border-radius:999px;justify-content:center;align-items:center;width:72px;height:72px;margin-bottom:18px;display:inline-flex}.room-setup-drop-icon.active{color:#1e293b;background:#dbeafe}.room-setup-drop-content h3{margin:0 0 8px;font-size:1.2rem}.room-setup-drop-content p,.room-setup-drop-content small{color:#64748b;margin:0}.room-setup-preview{width:100%;height:100%;position:relative}.room-setup-preview img{-o-object-fit:cover;object-fit:cover;width:100%;height:100%}.room-setup-preview-overlay{opacity:0;background:#ffffffd9;justify-content:center;align-items:center;transition:opacity .2s;display:flex;position:absolute;inset:0}.room-setup-preview:hover .room-setup-preview-overlay{opacity:1}.button-delete{color:#111827;background:#fff;border:1px solid #d1d5db;border-radius:14px;padding:12px 16px;font-weight:600}.room-setup-bottom h2{margin:0 0 24px}.room-setup-filters{flex-wrap:wrap;gap:10px;margin-bottom:24px;display:flex}.room-setup-filter{color:#334155;cursor:pointer;background:#fff;border:1px solid #d1d5db;border-radius:16px;padding:10px 16px;transition:all .2s}.room-setup-filter.active{background:#f3f4f6}.room-setup-grid{grid-template-columns:repeat(1,minmax(0,1fr));gap:20px;display:grid}.room-setup-card{background:#fff;border-radius:24px;overflow:hidden;box-shadow:0 8px 20px #0f172a0f}.room-setup-card-image{aspect-ratio:4/3;position:relative;overflow:hidden}.room-setup-card-image img{-o-object-fit:cover;object-fit:cover;width:100%;height:100%;transition:transform .4s}.room-setup-card:hover .room-setup-card-image img{transform:scale(1.04)}.room-setup-card h3{color:#334155;margin:16px;font-size:1rem}@media (width<=960px){.room-setup-top{grid-template-columns:1fr}.room-setup-grid{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width<=640px){.room-setup-inner{padding:20px 16px}.room-setup-grid{grid-template-columns:1fr}}.app-version-badge{color:#475569;text-align:right;z-index:1000;background:#fffffff2;border:1px solid #e5e7eb;border-radius:12px 0 0;padding:6px 10px;font-size:.78rem;position:fixed;bottom:0;right:0;box-shadow:0 8px 20px #0f172a14}.app-version-badge:hover{opacity:1}.app-version-badge span{color:#111827;font-weight:700;display:block}.app-version-badge small{color:#6b7280;margin-top:1px;display:block}@media (width<=640px){.app-version-badge{padding:5px 8px;font-size:.72rem;bottom:6px;right:6px}.app-version-badge small{margin-top:.5px}}@media (width<=900px){.content{grid-template-columns:1fr}}
frontend/dist/assets/index-DE9xapIa.js ADDED
The diff for this file is too large to render. See raw diff
 
frontend/dist/index.html CHANGED
@@ -4,7 +4,10 @@
4
  <meta charset="UTF-8" />
5
 
6
  <!-- Viewport: viewport-fit=cover extiende hasta los bordes en dispositivos con notch -->
7
- <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
 
 
 
8
 
9
  <!-- PWA -->
10
  <link rel="manifest" href="/manifest.json" />
@@ -12,7 +15,11 @@
12
 
13
  <!-- iOS PWA: fullscreen al instalarse desde Safari -->
14
  <meta name="apple-mobile-web-app-capable" content="yes" />
15
- <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
 
 
 
 
16
  <meta name="apple-mobile-web-app-title" content="Hyper Reality" />
17
  <link rel="apple-touch-icon" href="/favicon.svg" />
18
 
@@ -20,8 +27,8 @@
20
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
21
 
22
  <title>Hyper Reality Visualizer</title>
23
- <script type="module" crossorigin src="/assets/index-Ca5g_fre.js"></script>
24
- <link rel="stylesheet" crossorigin href="/assets/index-BDf3OMM_.css">
25
  </head>
26
  <body>
27
  <div id="root"></div>
 
4
  <meta charset="UTF-8" />
5
 
6
  <!-- Viewport: viewport-fit=cover extiende hasta los bordes en dispositivos con notch -->
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, viewport-fit=cover"
10
+ />
11
 
12
  <!-- PWA -->
13
  <link rel="manifest" href="/manifest.json" />
 
15
 
16
  <!-- iOS PWA: fullscreen al instalarse desde Safari -->
17
  <meta name="apple-mobile-web-app-capable" content="yes" />
18
+ <meta name="mobile-web-app-capable" content="yes" />
19
+ <meta
20
+ name="apple-mobile-web-app-status-bar-style"
21
+ content="black-translucent"
22
+ />
23
  <meta name="apple-mobile-web-app-title" content="Hyper Reality" />
24
  <link rel="apple-touch-icon" href="/favicon.svg" />
25
 
 
27
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
28
 
29
  <title>Hyper Reality Visualizer</title>
30
+ <script type="module" crossorigin src="/assets/index-DE9xapIa.js"></script>
31
+ <link rel="stylesheet" crossorigin href="/assets/index-CenEQfA2.css">
32
  </head>
33
  <body>
34
  <div id="root"></div>
frontend/index.html CHANGED
@@ -4,7 +4,10 @@
4
  <meta charset="UTF-8" />
5
 
6
  <!-- Viewport: viewport-fit=cover extiende hasta los bordes en dispositivos con notch -->
7
- <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
 
 
 
8
 
9
  <!-- PWA -->
10
  <link rel="manifest" href="/manifest.json" />
@@ -12,7 +15,11 @@
12
 
13
  <!-- iOS PWA: fullscreen al instalarse desde Safari -->
14
  <meta name="apple-mobile-web-app-capable" content="yes" />
15
- <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
 
 
 
 
16
  <meta name="apple-mobile-web-app-title" content="Hyper Reality" />
17
  <link rel="apple-touch-icon" href="/favicon.svg" />
18
 
 
4
  <meta charset="UTF-8" />
5
 
6
  <!-- Viewport: viewport-fit=cover extiende hasta los bordes en dispositivos con notch -->
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, viewport-fit=cover"
10
+ />
11
 
12
  <!-- PWA -->
13
  <link rel="manifest" href="/manifest.json" />
 
15
 
16
  <!-- iOS PWA: fullscreen al instalarse desde Safari -->
17
  <meta name="apple-mobile-web-app-capable" content="yes" />
18
+ <meta name="mobile-web-app-capable" content="yes" />
19
+ <meta
20
+ name="apple-mobile-web-app-status-bar-style"
21
+ content="black-translucent"
22
+ />
23
  <meta name="apple-mobile-web-app-title" content="Hyper Reality" />
24
  <link rel="apple-touch-icon" href="/favicon.svg" />
25
 
frontend/src/features/roomSetup/RoomSetup.tsx CHANGED
@@ -16,7 +16,10 @@ import useAppStore from "../../store/useAppStore";
16
  import { useHistoryStore, type HistoryItem } from "../../store/useAppStore";
17
  import { useActiveSessions } from "../../hooks/useActiveSessions";
18
  import { API_BASE } from "../../api/client";
19
- import { useLoadSessionHistory, deleteSessionFromBackend } from "../../hooks/useSessionSync";
 
 
 
20
  import { useCallback } from "react";
21
 
22
  export default function RoomSetup() {
@@ -222,8 +225,10 @@ export default function RoomSetup() {
222
  style={{ scrollbarWidth: "thin" }}
223
  >
224
  {sessionHistory.map((item: HistoryItem) => (
225
- <button
226
  key={item.filename}
 
 
227
  onClick={() =>
228
  navigate("/visualizer", {
229
  state: {
@@ -233,6 +238,17 @@ export default function RoomSetup() {
233
  },
234
  })
235
  }
 
 
 
 
 
 
 
 
 
 
 
236
  className={`flex-shrink-0 group relative rounded-2xl overflow-hidden border-2 transition-all duration-200 ${
237
  segmentFilename === item.filename
238
  ? "border-[#0047AB] shadow-lg shadow-[#0047AB]/20"
@@ -277,7 +293,7 @@ export default function RoomSetup() {
277
  Continuar <ArrowRight className="w-3.5 h-3.5" />
278
  </span>
279
  </div>
280
- </button>
281
  ))}
282
  </div>
283
  </section>
 
16
  import { useHistoryStore, type HistoryItem } from "../../store/useAppStore";
17
  import { useActiveSessions } from "../../hooks/useActiveSessions";
18
  import { API_BASE } from "../../api/client";
19
+ import {
20
+ useLoadSessionHistory,
21
+ deleteSessionFromBackend,
22
+ } from "../../hooks/useSessionSync";
23
  import { useCallback } from "react";
24
 
25
  export default function RoomSetup() {
 
225
  style={{ scrollbarWidth: "thin" }}
226
  >
227
  {sessionHistory.map((item: HistoryItem) => (
228
+ <div
229
  key={item.filename}
230
+ role="button"
231
+ tabIndex={0}
232
  onClick={() =>
233
  navigate("/visualizer", {
234
  state: {
 
238
  },
239
  })
240
  }
241
+ onKeyDown={(e) => {
242
+ if (e.key === "Enter" || e.key === " ") {
243
+ navigate("/visualizer", {
244
+ state: {
245
+ previewImage: item.previewUrl,
246
+ filename: item.filename,
247
+ maskCount: item.maskCount,
248
+ },
249
+ });
250
+ }
251
+ }}
252
  className={`flex-shrink-0 group relative rounded-2xl overflow-hidden border-2 transition-all duration-200 ${
253
  segmentFilename === item.filename
254
  ? "border-[#0047AB] shadow-lg shadow-[#0047AB]/20"
 
293
  Continuar <ArrowRight className="w-3.5 h-3.5" />
294
  </span>
295
  </div>
296
+ </div>
297
  ))}
298
  </div>
299
  </section>
frontend/src/features/roomSetup/roomSetupHooks.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
  } from "react";
9
  import { useNavigate } from "react-router-dom";
10
  import useAppStore, { useHistoryStore } from "../../store/useAppStore";
 
11
 
12
  export function useRoomSetup(): {
13
  isDragging: boolean;
@@ -54,6 +55,22 @@ export function useRoomSetup(): {
54
  setUploadMessage("Imagen lista — selecciona un producto para aplicar");
55
  setUploadedFile(file);
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  try {
58
  // Ahora el flujo es: usuario sube imagen → selecciona producto → backend /api/generate-image
59
  // Navegamos al visualizador con la preview local y el usuario selecciona producto allí.
 
8
  } from "react";
9
  import { useNavigate } from "react-router-dom";
10
  import useAppStore, { useHistoryStore } from "../../store/useAppStore";
11
+ import { uploadRoomImage, buildApiUrl } from "../../api/client";
12
 
13
  export function useRoomSetup(): {
14
  isDragging: boolean;
 
55
  setUploadMessage("Imagen lista — selecciona un producto para aplicar");
56
  setUploadedFile(file);
57
 
58
+ // Start uploading in background so backend has a stable URL for processing.
59
+ (async () => {
60
+ try {
61
+ const res = await uploadRoomImage(file);
62
+ if (res?.url) {
63
+ const serverUrl = buildApiUrl(res.url as string);
64
+ setPreviewImage(serverUrl);
65
+ setUploadMessage("Imagen subida al servidor");
66
+ }
67
+ } catch (err) {
68
+ const msg =
69
+ err instanceof Error ? err.message : "Error al subir la imagen";
70
+ setUploadMessage(msg);
71
+ }
72
+ })();
73
+
74
  try {
75
  // Ahora el flujo es: usuario sube imagen → selecciona producto → backend /api/generate-image
76
  // Navegamos al visualizador con la preview local y el usuario selecciona producto allí.
frontend/src/features/roomVisualizer/RoomVisualizer.tsx CHANGED
@@ -168,10 +168,31 @@ export default function RoomVisualizer() {
168
 
169
  const applyTextureWith = useCallback(
170
  async (texturePath: string) => {
 
 
 
 
 
 
 
 
171
  // Si el usuario subió una imagen en esta sesión, preferimos usar el endpoint OpenAI
172
  if (uploadedFile) {
173
  try {
174
- const data = await applyTextureOpenAI(uploadedFile, texturePath);
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  if (data?.url) {
176
  setCurrentPreviewImage(data.url);
177
  }
@@ -898,6 +919,51 @@ export default function RoomVisualizer() {
898
  flexDirection: "column",
899
  }}
900
  >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
901
  {isMobile ? (
902
  // ── Layout Mobile: imagen arriba, strip de thumbnails abajo ──────────
903
  <div
 
168
 
169
  const applyTextureWith = useCallback(
170
  async (texturePath: string) => {
171
+ if (isApplying) {
172
+ Swal.fire(
173
+ "Espera",
174
+ "Ya hay una operación en curso. Espera a que termine.",
175
+ "info",
176
+ );
177
+ return;
178
+ }
179
  // Si el usuario subió una imagen en esta sesión, preferimos usar el endpoint OpenAI
180
  if (uploadedFile) {
181
  try {
182
+ // If the image was already uploaded to server, prefer passing the server filename (avoid sending generated previews)
183
+ const serverUrl = state?.previewImage ?? storedPreviewImage ?? null;
184
+ let source: File | string = uploadedFile;
185
+ // If preview points to uploads path, extract filename and send it instead
186
+ if (
187
+ typeof serverUrl === "string" &&
188
+ serverUrl.includes("/uploads/")
189
+ ) {
190
+ // serverUrl may be e.g. "http://localhost:8000/uploads/<filename>" or "/uploads/<filename>"
191
+ const idx = serverUrl.lastIndexOf("/uploads/");
192
+ const filename = serverUrl.substring(idx + "/uploads/".length);
193
+ source = filename;
194
+ }
195
+ const data = await applyTextureOpenAI(source, texturePath);
196
  if (data?.url) {
197
  setCurrentPreviewImage(data.url);
198
  }
 
919
  flexDirection: "column",
920
  }}
921
  >
922
+ {isApplying && (
923
+ <div
924
+ aria-hidden={!isApplying}
925
+ style={{
926
+ position: "fixed",
927
+ inset: 0,
928
+ display: "flex",
929
+ alignItems: "center",
930
+ justifyContent: "center",
931
+ background: "rgba(0,0,0,0.45)",
932
+ zIndex: 9999,
933
+ backdropFilter: "blur(2px)",
934
+ WebkitBackdropFilter: "blur(2px)",
935
+ }}
936
+ >
937
+ <div
938
+ style={{
939
+ display: "flex",
940
+ flexDirection: "column",
941
+ alignItems: "center",
942
+ gap: 12,
943
+ padding: 20,
944
+ borderRadius: 12,
945
+ background: "rgba(255,255,255,0.98)",
946
+ boxShadow: "0 6px 24px rgba(0,0,0,0.3)",
947
+ minWidth: 220,
948
+ }}
949
+ >
950
+ <div
951
+ style={{
952
+ width: 48,
953
+ height: 48,
954
+ borderRadius: 24,
955
+ border: "4px solid #e6e6e6",
956
+ borderTopColor: "#0047AB",
957
+ animation: "hr-spin 1s linear infinite",
958
+ }}
959
+ />
960
+ <div style={{ fontSize: 14, color: "#111", textAlign: "center" }}>
961
+ Generando imagen… Por favor espera
962
+ </div>
963
+ </div>
964
+ <style>{`@keyframes hr-spin { from { transform: rotate(0deg) } to { transform: rotate(360deg) } }`}</style>
965
+ </div>
966
+ )}
967
  {isMobile ? (
968
  // ── Layout Mobile: imagen arriba, strip de thumbnails abajo ──────────
969
  <div
frontend/src/hooks/useApplyTexture.ts CHANGED
@@ -1,5 +1,6 @@
1
  import { useCallback, useState } from "react";
2
  import { API_BASE } from "../api/client";
 
3
 
4
  interface ApplyTextureResult {
5
  output_url: string;
@@ -125,13 +126,31 @@ export function useApplyTexture() {
125
  );
126
 
127
  const applyTextureOpenAI = useCallback(
128
- async (file: File, textureName: string, apiKey?: string | null) => {
 
 
 
 
 
129
  setIsApplying(true);
130
  setError(null);
131
  try {
132
  const form = new FormData();
133
- form.append("file", file);
 
 
 
 
 
 
 
 
 
134
  form.append("texture", textureName);
 
 
 
 
135
  if (apiKey) form.append("api_key", apiKey);
136
 
137
  const res = await fetch(`${API_BASE}/api/generate-image`, {
 
1
  import { useCallback, useState } from "react";
2
  import { API_BASE } from "../api/client";
3
+ import { useHistoryStore } from "../store/useAppStore";
4
 
5
  interface ApplyTextureResult {
6
  output_url: string;
 
126
  );
127
 
128
  const applyTextureOpenAI = useCallback(
129
+ async (
130
+ fileOrFilename: File | string,
131
+ textureName: string,
132
+ apiKey?: string | null,
133
+ ) => {
134
+ if (isApplying) throw new Error("Ya hay una operación en curso");
135
  setIsApplying(true);
136
  setError(null);
137
  try {
138
  const form = new FormData();
139
+ // Accept either a File object (client-side upload) or a server filename (string)
140
+ if (typeof fileOrFilename === "string") {
141
+ // server-side filename returned by uploadRoomImage, e.g. "/uploads/abcd.png" or full URL
142
+ const name = fileOrFilename.includes("/uploads/")
143
+ ? fileOrFilename.split("/uploads/").pop() || fileOrFilename
144
+ : fileOrFilename;
145
+ form.append("source_filename", name);
146
+ } else {
147
+ form.append("file", fileOrFilename);
148
+ }
149
  form.append("texture", textureName);
150
+ const userId =
151
+ useHistoryStore.getState?.()?.userId ??
152
+ useHistoryStore((s) => s.userId);
153
+ if (userId) form.append("user_id", userId);
154
  if (apiKey) form.append("api_key", apiKey);
155
 
156
  const res = await fetch(`${API_BASE}/api/generate-image`, {
frontend/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const appVersion = "0.1.0-dev.20260512T005545";
 
1
+ export const appVersion = "0.1.0-dev.20260512T030125";