OnyxlMunkey commited on
Commit
cc036eb
·
1 Parent(s): 0f5ca68

Fix: use same-origin proxy for HF inference (avoid CORS)

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.sh text eol=lf
Dockerfile CHANGED
@@ -32,21 +32,36 @@ RUN npm run build
32
  # Stage 2: Serve the application with Nginx
33
  FROM nginx:alpine
34
 
 
 
35
  COPY --from=builder /app/dist /usr/share/nginx/html
 
36
 
37
- # Copy custom nginx config if needed (optional but good practice for SPAs)
38
- # Creating a simple config inline to handle client-side routing fallback
39
  RUN echo 'server { \
40
  listen 7860; \
 
 
 
 
 
 
 
 
 
41
  location / { \
42
  root /usr/share/nginx/html; \
43
  index index.html index.htm; \
44
  try_files $uri $uri/ /index.html; \
45
  } \
46
- }' > /etc/nginx/conf.d/default.conf
 
 
 
 
 
 
 
47
 
48
- # Expose port 7860 for Hugging Face Spaces
49
  EXPOSE 7860
50
 
51
- # Start Nginx
52
- CMD ["nginx", "-g", "daemon off;"]
 
32
  # Stage 2: Serve the application with Nginx
33
  FROM nginx:alpine
34
 
35
+ RUN adduser -D -u 1000 appuser
36
+
37
  COPY --from=builder /app/dist /usr/share/nginx/html
38
+ COPY entrypoint.sh /entrypoint.sh
39
 
 
 
40
  RUN echo 'server { \
41
  listen 7860; \
42
+ location /api/hf-inference/ { \
43
+ rewrite ^/api/hf-inference/(.*)$ /$1 break; \
44
+ proxy_pass https://api-inference.huggingface.co; \
45
+ proxy_http_version 1.1; \
46
+ proxy_set_header Host api-inference.huggingface.co; \
47
+ proxy_set_header Authorization "Bearer __VITE_HF_TOKEN__"; \
48
+ proxy_pass_request_headers on; \
49
+ proxy_buffering off; \
50
+ } \
51
  location / { \
52
  root /usr/share/nginx/html; \
53
  index index.html index.htm; \
54
  try_files $uri $uri/ /index.html; \
55
  } \
56
+ }' > /etc/nginx/conf.d/default.conf.tpl
57
+ RUN sed 's|__VITE_HF_TOKEN__||g' /etc/nginx/conf.d/default.conf.tpl > /etc/nginx/conf.d/default.conf
58
+
59
+ RUN chmod +x /entrypoint.sh && \
60
+ chown -R appuser:appuser /usr/share/nginx/html /var/cache/nginx /var/log/nginx /etc/nginx/conf.d && \
61
+ touch /var/run/nginx.pid && chown appuser:appuser /var/run/nginx.pid
62
+
63
+ USER appuser
64
 
 
65
  EXPOSE 7860
66
 
67
+ ENTRYPOINT ["/entrypoint.sh"]
 
entrypoint.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ # Inject runtime env and nginx proxy config (avoids CORS: browser hits same origin, nginx forwards to HF with token)
3
+ TOKEN="${VITE_HF_TOKEN:-}"
4
+ ESCAPED=$(printf '%s' "$TOKEN" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\$/\\$/g')
5
+ printf 'window.__HF_ENV={"VITE_HF_TOKEN":"%s"};\n' "$ESCAPED" > /usr/share/nginx/html/config.js
6
+
7
+ # Substitute token into nginx config (proxy avoids CORS)
8
+ sed "s|__VITE_HF_TOKEN__|$TOKEN|g" /etc/nginx/conf.d/default.conf.tpl > /etc/nginx/conf.d/default.conf
9
+
10
+ exec nginx -g "daemon off;"
index.html CHANGED
@@ -7,6 +7,8 @@
7
  </head>
8
  <body>
9
  <div id="root"></div>
 
 
10
  <script type="module" src="/src/main.jsx"></script>
11
  </body>
12
  </html>
 
7
  </head>
8
  <body>
9
  <div id="root"></div>
10
+ <script>window.__HF_ENV=window.__HF_ENV||{};</script>
11
+ <script src="/config.js"></script>
12
  <script type="module" src="/src/main.jsx"></script>
13
  </body>
14
  </html>
scripts/debug-space.js ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Playwright script: open HF Space, trigger TinyLlama, capture console/network errors.
3
+ * Run: node scripts/debug-space.js
4
+ * Or with auth URL: SPACE_URL="https://user:TOKEN@huggingface.co/spaces/OnyxMunk/text-transformer" node scripts/debug-space.js
5
+ */
6
+ const { chromium } = require('playwright');
7
+
8
+ const SPACE_URL = process.env.SPACE_URL || 'https://huggingface.co/spaces/OnyxMunk/text-transformer';
9
+
10
+ async function main() {
11
+ const browser = await chromium.launch({ headless: false });
12
+ const context = await browser.newContext();
13
+ const consoleLogs = [];
14
+ const requestFailures = [];
15
+
16
+ context.on('console', (msg) => {
17
+ const text = msg.text();
18
+ const type = msg.type();
19
+ consoleLogs.push({ type, text });
20
+ if (type === 'error' || text.toLowerCase().includes('failed') || text.toLowerCase().includes('cors')) {
21
+ console.error(`[CONSOLE ${type}]`, text);
22
+ }
23
+ });
24
+
25
+ context.on('requestfailed', (request) => {
26
+ const failure = { url: request.url(), failure: request.failure()?.errorText };
27
+ requestFailures.push(failure);
28
+ console.error('[REQUEST FAILED]', failure.url, failure.failure);
29
+ });
30
+
31
+ const page = await context.newPage();
32
+ await page.goto(SPACE_URL, { waitUntil: 'domcontentloaded', timeout: 60000 }).catch((e) => {
33
+ console.error('Navigate error:', e.message);
34
+ });
35
+
36
+ await page.waitForTimeout(5000);
37
+
38
+ const snapshot = await page.content();
39
+ if (snapshot.includes('Failed to connect') || snapshot.includes('CORS') || snapshot.includes('error')) {
40
+ const match = snapshot.match(/>([^<]*(?:Failed to connect|CORS|error)[^<]*)</);
41
+ if (match) console.error('Page contains error text:', match[1].trim().slice(0, 200));
42
+ }
43
+
44
+ const tinyLlamaTab = page.locator('button, [role="tab"], a').filter({ hasText: /TinyLlama|Tiny Llama/i }).first();
45
+ if (await tinyLlamaTab.count() > 0) {
46
+ await tinyLlamaTab.click();
47
+ await page.waitForTimeout(1500);
48
+ const promptInput = page.locator('textarea[placeholder*="TinyLlama"], textarea[id="llama-input"], textarea').first();
49
+ if (await promptInput.count() > 0) {
50
+ await promptInput.fill('Hello');
51
+ await page.waitForTimeout(500);
52
+ const generateBtn = page.locator('button[title="Generate Response"], button').filter({ hasText: /Generate|⚡/ }).first();
53
+ if (await generateBtn.count() > 0) {
54
+ await generateBtn.click();
55
+ await page.waitForTimeout(8000);
56
+ }
57
+ }
58
+ }
59
+
60
+ const errorEl = page.locator('text=/Failed to connect|CORS|error/i').first();
61
+ if (await errorEl.count() > 0) {
62
+ console.error('Visible error on page:', await errorEl.textContent());
63
+ }
64
+
65
+ console.log('\n--- Console messages (errors/warnings) ---');
66
+ consoleLogs.filter((m) => m.type === 'error' || m.type === 'warning').forEach((m) => console.log(m.type, m.text));
67
+ console.log('\n--- Failed network requests ---');
68
+ requestFailures.forEach((f) => console.log(f.url, f.failure));
69
+
70
+ await browser.close();
71
+ }
72
+
73
+ main().catch((e) => {
74
+ console.error(e);
75
+ process.exit(1);
76
+ });
src/components/TinyLlamaConverter.jsx CHANGED
@@ -12,7 +12,9 @@ function getTinyLlamaConfig() {
12
  if (typeof window === 'undefined') return { provider: 'ollama', baseUrl: OLLAMA_BASE };
13
  const host = window.location.hostname;
14
  if (host === 'localhost' || host === '127.0.0.1') return { provider: 'ollama', baseUrl: OLLAMA_BASE };
15
- if (host.includes('huggingface.co') || host.includes('hf.space')) return { provider: 'hf', baseUrl: 'https://api-inference.huggingface.co', token: env.VITE_HF_TOKEN };
 
 
16
  if (host.includes('vercel.app')) {
17
  const vercelUrl = env.VITE_TINYLLAMA_VERCEL_URL?.trim();
18
  return { provider: 'ollama', baseUrl: vercelUrl || OLLAMA_BASE };
@@ -39,12 +41,10 @@ const TinyLlamaConverter = ({ sharedInput, setSharedInput }) => {
39
 
40
  try {
41
  if (config.provider === 'hf') {
42
- const url = `${config.baseUrl}/models/${HF_MODEL}`;
43
- const headers = { 'Content-Type': 'application/json' };
44
- if (config.token) headers['Authorization'] = `Bearer ${config.token}`;
45
  const res = await fetch(url, {
46
  method: 'POST',
47
- headers,
48
  body: JSON.stringify({
49
  inputs: sharedInput.trim(),
50
  parameters: { max_new_tokens: 512, return_full_text: false },
@@ -52,7 +52,9 @@ const TinyLlamaConverter = ({ sharedInput, setSharedInput }) => {
52
  });
53
  if (!res.ok) {
54
  const errBody = await res.text();
55
- throw new Error(res.status === 401 ? 'Hugging Face token missing or invalid. Set VITE_HF_TOKEN.' : errBody || res.statusText);
 
 
56
  }
57
  const data = await res.json();
58
  const text = Array.isArray(data) ? data[0]?.generated_text : data?.generated_text;
@@ -82,7 +84,9 @@ const TinyLlamaConverter = ({ sharedInput, setSharedInput }) => {
82
  if (err.message.includes("Failed to fetch")) {
83
  const msg = config.provider === 'ollama' && config.baseUrl === OLLAMA_BASE
84
  ? "Failed to connect to Ollama. Ensure it's running on port 11434 and CORS is allowed (OLLAMA_ORIGINS=\"*\")."
85
- : `Failed to connect to ${config.baseUrl}. Check network and CORS.`;
 
 
86
  setError(msg);
87
  } else {
88
  setError(err.message);
 
12
  if (typeof window === 'undefined') return { provider: 'ollama', baseUrl: OLLAMA_BASE };
13
  const host = window.location.hostname;
14
  if (host === 'localhost' || host === '127.0.0.1') return { provider: 'ollama', baseUrl: OLLAMA_BASE };
15
+ if (host.includes('huggingface.co') || host.includes('hf.space')) {
16
+ return { provider: 'hf', baseUrl: '/api/hf-inference', token: null };
17
+ }
18
  if (host.includes('vercel.app')) {
19
  const vercelUrl = env.VITE_TINYLLAMA_VERCEL_URL?.trim();
20
  return { provider: 'ollama', baseUrl: vercelUrl || OLLAMA_BASE };
 
41
 
42
  try {
43
  if (config.provider === 'hf') {
44
+ const url = `/api/hf-inference/models/${HF_MODEL}`;
 
 
45
  const res = await fetch(url, {
46
  method: 'POST',
47
+ headers: { 'Content-Type': 'application/json' },
48
  body: JSON.stringify({
49
  inputs: sharedInput.trim(),
50
  parameters: { max_new_tokens: 512, return_full_text: false },
 
52
  });
53
  if (!res.ok) {
54
  const errBody = await res.text();
55
+ throw new Error(res.status === 401
56
+ ? 'Hugging Face token missing or invalid. Add VITE_HF_TOKEN in Space Settings → Variables and secrets, then restart.'
57
+ : errBody || res.statusText);
58
  }
59
  const data = await res.json();
60
  const text = Array.isArray(data) ? data[0]?.generated_text : data?.generated_text;
 
84
  if (err.message.includes("Failed to fetch")) {
85
  const msg = config.provider === 'ollama' && config.baseUrl === OLLAMA_BASE
86
  ? "Failed to connect to Ollama. Ensure it's running on port 11434 and CORS is allowed (OLLAMA_ORIGINS=\"*\")."
87
+ : config.provider === 'hf'
88
+ ? "CORS or network error. Ensure the latest code is deployed (with /api/hf-inference proxy) and VITE_HF_TOKEN is set in Space Settings → Secrets, then restart."
89
+ : `Failed to connect to ${config.baseUrl}. Check network and CORS.`;
90
  setError(msg);
91
  } else {
92
  setError(err.message);