Spaces:
Paused
Paused
Upload 19 files
Browse files- .gitignore +4 -0
- Dockerfile +19 -8
- client/index.html +12 -0
- client/package.json +23 -0
- client/src/App.tsx +3 -0
- client/src/api.ts +4 -0
- client/src/main.tsx +5 -0
- client/src/scene/Scene.tsx +108 -0
- client/src/styles.css +2 -0
- client/tsconfig.json +11 -0
- docker-compose.yml +8 -0
- package.json +7 -7
- server/.env.example +5 -0
- server/package.json +22 -0
- server/src/index.ts +11 -0
- server/src/routes/api.ts +16 -0
- server/src/services/gemini.ts +13 -0
- server/src/services/hf.ts +11 -0
- server/tsconfig.json +12 -0
.gitignore
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
node_modules
|
| 2 |
+
dist
|
| 3 |
+
.env
|
| 4 |
+
.vscode
|
Dockerfile
CHANGED
|
@@ -1,13 +1,24 @@
|
|
| 1 |
-
FROM node:20-alpine
|
| 2 |
WORKDIR /app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
RUN npm install --production
|
| 7 |
-
|
| 8 |
COPY . .
|
|
|
|
|
|
|
| 9 |
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
EXPOSE 4000
|
| 13 |
-
CMD ["node",
|
|
|
|
| 1 |
+
FROM node:20-alpine AS deps
|
| 2 |
WORKDIR /app
|
| 3 |
+
COPY package.json .
|
| 4 |
+
COPY server/package.json ./server/package.json
|
| 5 |
+
COPY client/package.json ./client/package.json
|
| 6 |
+
RUN apk add --no-cache git
|
| 7 |
+
RUN cd server && npm install
|
| 8 |
+
RUN cd ../client && npm install
|
| 9 |
|
| 10 |
+
FROM node:20-alpine AS builder
|
| 11 |
+
WORKDIR /app
|
|
|
|
|
|
|
| 12 |
COPY . .
|
| 13 |
+
RUN cd client && npm run build
|
| 14 |
+
RUN cd server && npm run build
|
| 15 |
|
| 16 |
+
FROM node:20-alpine AS runner
|
| 17 |
+
WORKDIR /app
|
| 18 |
+
ENV NODE_ENV=production
|
| 19 |
+
COPY --from=builder /app/server/dist ./server/dist
|
| 20 |
+
COPY --from=builder /app/client/dist ./client/dist
|
| 21 |
+
COPY server/package.json ./server/package.json
|
| 22 |
+
RUN cd server && npm install --production
|
| 23 |
EXPOSE 4000
|
| 24 |
+
CMD ["node","server/dist/index.js"]
|
client/index.html
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>Neuro Galaxies</title>
|
| 7 |
+
</head>
|
| 8 |
+
<body>
|
| 9 |
+
<div id="root"></div>
|
| 10 |
+
<script type="module" src="/src/main.tsx"></script>
|
| 11 |
+
</body>
|
| 12 |
+
</html>
|
client/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "neuro-client",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"private": true,
|
| 5 |
+
"type": "module",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"dev": "vite",
|
| 8 |
+
"build": "vite build",
|
| 9 |
+
"preview": "vite preview"
|
| 10 |
+
},
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"react": "^18.2.0",
|
| 13 |
+
"react-dom": "^18.2.0",
|
| 14 |
+
"three": "^0.152.2"
|
| 15 |
+
},
|
| 16 |
+
"devDependencies": {
|
| 17 |
+
"typescript": "^5.5.0",
|
| 18 |
+
"vite": "^5.0.0",
|
| 19 |
+
"@vitejs/plugin-react": "^4.2.1",
|
| 20 |
+
"@types/react": "^18.2.28",
|
| 21 |
+
"@types/react-dom": "^18.2.11"
|
| 22 |
+
}
|
| 23 |
+
}
|
client/src/App.tsx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react'
|
| 2 |
+
import Scene from './scene/Scene'
|
| 3 |
+
export default function App(){ return <Scene/> }
|
client/src/api.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export async function generate(prompt:string){
|
| 2 |
+
const res = await fetch('/api/generate',{ method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ prompt }) })
|
| 3 |
+
return res.json()
|
| 4 |
+
}
|
client/src/main.tsx
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React from 'react'
|
| 2 |
+
import { createRoot } from 'react-dom/client'
|
| 3 |
+
import App from './App'
|
| 4 |
+
import './styles.css'
|
| 5 |
+
createRoot(document.getElementById('root')!).render(<App />)
|
client/src/scene/Scene.tsx
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import React, { useEffect, useRef, useState } from 'react'
|
| 2 |
+
import * as THREE from 'three'
|
| 3 |
+
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
|
| 4 |
+
export default function Scene(): JSX.Element {
|
| 5 |
+
const mountRef = useRef<HTMLDivElement|null>(null)
|
| 6 |
+
const [ready,setReady] = useState(false)
|
| 7 |
+
useEffect(()=>{
|
| 8 |
+
const container = mountRef.current!
|
| 9 |
+
const scene = new THREE.Scene()
|
| 10 |
+
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000)
|
| 11 |
+
camera.position.set(0,0,20)
|
| 12 |
+
const renderer = new THREE.WebGLRenderer({ antialias:true })
|
| 13 |
+
renderer.setSize(window.innerWidth, window.innerHeight)
|
| 14 |
+
renderer.setPixelRatio(window.devicePixelRatio)
|
| 15 |
+
container.appendChild(renderer.domElement)
|
| 16 |
+
const controls = new OrbitControls(camera, renderer.domElement)
|
| 17 |
+
controls.enableDamping = true
|
| 18 |
+
const ambient = new THREE.AmbientLight(0xffffff,0.7); scene.add(ambient)
|
| 19 |
+
const dir = new THREE.DirectionalLight(0xffffff,0.6); dir.position.set(5,10,7.5); scene.add(dir)
|
| 20 |
+
const group = new THREE.Group(); scene.add(group)
|
| 21 |
+
// comet particles system
|
| 22 |
+
const trails: {mesh:THREE.Mesh, life:number}[] = []
|
| 23 |
+
function addComet(word:string){
|
| 24 |
+
const geom = new THREE.SphereGeometry(0.12,8,8)
|
| 25 |
+
const mat = new THREE.MeshStandardMaterial({ color: new THREE.Color(`hsl(${Math.random()*360},80%,60%)`), emissive:0x111111 })
|
| 26 |
+
const m = new THREE.Mesh(geom, mat)
|
| 27 |
+
m.position.set((Math.random()-0.5)*30, (Math.random()-0.5)*20, (Math.random()-0.5)*30)
|
| 28 |
+
(m.userData as any).vel = new THREE.Vector3((Math.random()-0.5)*0.6, (Math.random()-0.5)*0.6, (Math.random()-0.5)*0.6)
|
| 29 |
+
(m.userData as any).word = word
|
| 30 |
+
group.add(m)
|
| 31 |
+
trails.push({mesh:m, life:120})
|
| 32 |
+
}
|
| 33 |
+
// text labels as sprites
|
| 34 |
+
const spriteMaterial = new THREE.SpriteMaterial({ color: 0xffffff })
|
| 35 |
+
function addLabel(text:string, pos:THREE.Vector3){
|
| 36 |
+
const canvas = document.createElement('canvas')
|
| 37 |
+
canvas.width = 256; canvas.height = 64
|
| 38 |
+
const ctx = canvas.getContext('2d')!
|
| 39 |
+
ctx.fillStyle = 'rgba(255,255,255,0.9)'; ctx.font = '30px sans-serif'
|
| 40 |
+
ctx.fillText(text, 8,40)
|
| 41 |
+
const tex = new THREE.CanvasTexture(canvas)
|
| 42 |
+
const mat = new THREE.SpriteMaterial({ map:tex, transparent:true })
|
| 43 |
+
const sp = new THREE.Sprite(mat)
|
| 44 |
+
sp.scale.set(4,1,1); sp.position.copy(pos)
|
| 45 |
+
(sp.userData as any).isLabel = true
|
| 46 |
+
group.add(sp)
|
| 47 |
+
}
|
| 48 |
+
// generate initial galaxy nodes from demo algorithm
|
| 49 |
+
function spawnGalaxy(topic:string, origin:THREE.Vector3){
|
| 50 |
+
const mainCount = Math.max(3, Math.min(12, Math.floor(Math.random()*10)))
|
| 51 |
+
for(let i=0;i<mainCount;i++){
|
| 52 |
+
const theta = Math.random()*Math.PI*2
|
| 53 |
+
const r = 6 + Math.random()*6
|
| 54 |
+
const x = origin.x + Math.cos(theta)*r
|
| 55 |
+
const y = origin.y + (Math.random()-0.5)*4
|
| 56 |
+
const z = origin.z + Math.sin(theta)*r
|
| 57 |
+
const node = new THREE.Mesh(new THREE.SphereGeometry(0.25,10,10), new THREE.MeshStandardMaterial({ color: new THREE.Color(`hsl(${(i/mainCount)*360},80%,60%)`) }))
|
| 58 |
+
node.position.set(x,y,z)
|
| 59 |
+
group.add(node)
|
| 60 |
+
addLabel('n'+i, node.position.clone().add(new THREE.Vector3(0.6,0.6,0)))
|
| 61 |
+
// create comets passing near node
|
| 62 |
+
if(Math.random()<0.6){
|
| 63 |
+
addComet('⭐')
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
}
|
| 67 |
+
// add a root
|
| 68 |
+
const root = new THREE.Mesh(new THREE.SphereGeometry(0.5,16,16), new THREE.MeshStandardMaterial({ color: 0x66ffcc }))
|
| 69 |
+
root.position.set(0,0,0); group.add(root); addLabel('ROOT', root.position.clone().add(new THREE.Vector3(0,1,0)))
|
| 70 |
+
spawnGalaxy('example', new THREE.Vector3(0,0,0))
|
| 71 |
+
// comet update and trail rendering (simple)
|
| 72 |
+
const trailGroup = new THREE.Group(); scene.add(trailGroup)
|
| 73 |
+
function animate(){
|
| 74 |
+
requestAnimationFrame(animate)
|
| 75 |
+
controls.update()
|
| 76 |
+
// update comets
|
| 77 |
+
for(let i=trails.length-1;i>=0;i--){
|
| 78 |
+
const t = trails[i]
|
| 79 |
+
t.mesh.position.add((t.mesh.userData as any).vel)
|
| 80 |
+
t.life -= 1
|
| 81 |
+
// spawn small fading dots to simulate trail
|
| 82 |
+
const dot = new THREE.Mesh(new THREE.SphereGeometry(0.04,6,6), new THREE.MeshBasicMaterial({ color: (t.mesh.material as any).color, transparent:true, opacity:0.6 }))
|
| 83 |
+
dot.position.copy(t.mesh.position)
|
| 84 |
+
trailGroup.add(dot)
|
| 85 |
+
// fade dots
|
| 86 |
+
setTimeout(()=>{ try{ trailGroup.remove(dot); dot.geometry.dispose(); (dot.material as any).dispose() }catch(e){} }, 900)
|
| 87 |
+
if(t.life<=0){
|
| 88 |
+
try{ group.remove(t.mesh); t.mesh.geometry.dispose(); (t.mesh.material as any).dispose() }catch(e){}
|
| 89 |
+
trails.splice(i,1)
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
renderer.render(scene, camera)
|
| 93 |
+
}
|
| 94 |
+
animate()
|
| 95 |
+
const onResize = ()=>{ camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight) }
|
| 96 |
+
window.addEventListener('resize', onResize)
|
| 97 |
+
setReady(true)
|
| 98 |
+
return ()=>{
|
| 99 |
+
window.removeEventListener('resize', onResize)
|
| 100 |
+
try{ container.removeChild(renderer.domElement) }catch(e){}
|
| 101 |
+
}
|
| 102 |
+
},[])
|
| 103 |
+
return <div style={{width:'100vw',height:'100vh',position:'relative'}} ref={mountRef}>
|
| 104 |
+
<div style={{position:'absolute',left:20,top:20,zIndex:3,background:'rgba(10,10,10,0.6)',padding:12,borderRadius:8}}>
|
| 105 |
+
<input id="topic" placeholder="hashtag or topic" style={{width:300}} />
|
| 106 |
+
</div>
|
| 107 |
+
</div>
|
| 108 |
+
}
|
client/src/styles.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
:root{--bg:#0b1020}
|
| 2 |
+
html,body,#root{height:100%;margin:0;background:var(--bg);color:#fff;font-family:Inter,system-ui}
|
client/tsconfig.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"target": "ES2022",
|
| 4 |
+
"module": "ESNext",
|
| 5 |
+
"jsx": "react-jsx",
|
| 6 |
+
"moduleResolution": "Node",
|
| 7 |
+
"strict": true,
|
| 8 |
+
"esModuleInterop": true
|
| 9 |
+
},
|
| 10 |
+
"include": ["src"]
|
| 11 |
+
}
|
docker-compose.yml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: '3.8'
|
| 2 |
+
services:
|
| 3 |
+
app:
|
| 4 |
+
build: .
|
| 5 |
+
env_file:
|
| 6 |
+
- server/.env.example
|
| 7 |
+
ports:
|
| 8 |
+
- "4000:4000"
|
package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
| 1 |
{
|
| 2 |
-
"name": "
|
| 3 |
"private": true,
|
| 4 |
-
"workspaces": [
|
|
|
|
|
|
|
|
|
|
| 5 |
"scripts": {
|
| 6 |
-
"
|
| 7 |
-
"install:all": "pnpm install --recursive"
|
| 8 |
},
|
| 9 |
-
"devDependencies": {
|
| 10 |
-
"concurrently": "^8.2.0"
|
| 11 |
-
}
|
| 12 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"name": "neuro-galaxy-monorepo",
|
| 3 |
"private": true,
|
| 4 |
+
"workspaces": [
|
| 5 |
+
"client",
|
| 6 |
+
"server"
|
| 7 |
+
],
|
| 8 |
"scripts": {
|
| 9 |
+
"start": "echo use workspace scripts"
|
|
|
|
| 10 |
},
|
| 11 |
+
"devDependencies": {}
|
|
|
|
|
|
|
| 12 |
}
|
server/.env.example
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
HF_API_KEY=
|
| 2 |
+
GEMINI_API_KEY=
|
| 3 |
+
MODEL_PROVIDER=hf
|
| 4 |
+
PORT=4000
|
| 5 |
+
FIREBASE_SERVICE_ACCOUNT=
|
server/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "neuro-server",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"type": "module",
|
| 5 |
+
"scripts": {
|
| 6 |
+
"dev": "ts-node-dev --respawn --transpile-only src/index.ts",
|
| 7 |
+
"build": "tsc -p .",
|
| 8 |
+
"start": "node dist/index.js"
|
| 9 |
+
},
|
| 10 |
+
"dependencies": {
|
| 11 |
+
"express": "^4.18.2",
|
| 12 |
+
"cors": "^2.8.5",
|
| 13 |
+
"dotenv": "^16.3.1",
|
| 14 |
+
"node-fetch": "^3.4.2"
|
| 15 |
+
},
|
| 16 |
+
"devDependencies": {
|
| 17 |
+
"typescript": "^5.5.0",
|
| 18 |
+
"ts-node-dev": "^2.0.0",
|
| 19 |
+
"@types/node": "^20.5.6",
|
| 20 |
+
"@types/express": "^4.17.21"
|
| 21 |
+
}
|
| 22 |
+
}
|
server/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import express from 'express'
|
| 2 |
+
import cors from 'cors'
|
| 3 |
+
import dotenv from 'dotenv'
|
| 4 |
+
import apiRouter from './routes/api.js'
|
| 5 |
+
dotenv.config()
|
| 6 |
+
const app = express()
|
| 7 |
+
app.use(cors())
|
| 8 |
+
app.use(express.json())
|
| 9 |
+
app.use('/api', apiRouter)
|
| 10 |
+
const port = Number(process.env.PORT||4000)
|
| 11 |
+
app.listen(port, ()=>{ console.log('server listening', port) })
|
server/src/routes/api.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Router } from 'express'
|
| 2 |
+
import { proxyHF } from '../services/hf.js'
|
| 3 |
+
import { proxyGemini } from '../services/gemini.js'
|
| 4 |
+
const router = Router()
|
| 5 |
+
router.post('/generate', async (req, res) => {
|
| 6 |
+
const { prompt } = req.body
|
| 7 |
+
if (!prompt) return res.status(400).json({ error: 'prompt required' })
|
| 8 |
+
try {
|
| 9 |
+
const provider = process.env.MODEL_PROVIDER || 'hf'
|
| 10 |
+
const out = provider === 'gemini' ? await proxyGemini(prompt) : await proxyHF(prompt)
|
| 11 |
+
res.json({ output: out })
|
| 12 |
+
} catch (e) {
|
| 13 |
+
res.status(500).json({ error: String(e) })
|
| 14 |
+
}
|
| 15 |
+
})
|
| 16 |
+
export default router
|
server/src/services/gemini.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fetch from 'node-fetch'
|
| 2 |
+
const KEY = process.env.GEMINI_API_KEY
|
| 3 |
+
const BASE = 'https://generativelanguage.googleapis.com/v1beta2/models'
|
| 4 |
+
export async function proxyGemini(prompt:string){
|
| 5 |
+
if(!KEY) throw new Error('GEMINI_API_KEY missing')
|
| 6 |
+
const model = process.env.GEMINI_MODEL||'models/text-bison-001'
|
| 7 |
+
const url = `${BASE}/${model}:generate`
|
| 8 |
+
const res = await fetch(url, { method:'POST', headers: { Authorization:`Bearer ${KEY}`, 'Content-Type':'application/json' }, body: JSON.stringify({ prompt }) })
|
| 9 |
+
const j = await res.json()
|
| 10 |
+
if(j?.candidates?.[0]?.content) return j.candidates[0].content
|
| 11 |
+
if(j?.outputText) return j.outputText
|
| 12 |
+
return JSON.stringify(j)
|
| 13 |
+
}
|
server/src/services/hf.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fetch from 'node-fetch'
|
| 2 |
+
const KEY = process.env.HF_API_KEY
|
| 3 |
+
const MODEL = process.env.HF_DEFAULT_MODEL || 'gpt2'
|
| 4 |
+
const URL = `https://api-inference.huggingface.co/models/${MODEL}`
|
| 5 |
+
export async function proxyHF(prompt:string){
|
| 6 |
+
if(!KEY) throw new Error('HF_API_KEY missing')
|
| 7 |
+
const r = await fetch(URL, { method: 'POST', headers: { Authorization: `Bearer ${KEY}`, 'Content-Type':'application/json' }, body: JSON.stringify({ inputs: prompt }) })
|
| 8 |
+
const j = await r.json()
|
| 9 |
+
if(Array.isArray(j) && j[0]?.generated_text) return j[0].generated_text
|
| 10 |
+
return j.generated_text || JSON.stringify(j)
|
| 11 |
+
}
|
server/tsconfig.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"compilerOptions": {
|
| 3 |
+
"outDir": "dist",
|
| 4 |
+
"rootDir": "src",
|
| 5 |
+
"module": "ESNext",
|
| 6 |
+
"target": "ES2022",
|
| 7 |
+
"moduleResolution": "Node",
|
| 8 |
+
"strict": true,
|
| 9 |
+
"esModuleInterop": true
|
| 10 |
+
},
|
| 11 |
+
"include": ["src"]
|
| 12 |
+
}
|