suraj140 commited on
Commit
1a1fbdc
Β·
1 Parent(s): a67f08f

Add OpenEnv API endpoints and fix reset/step/state support for Phase 1 checks

Browse files
Files changed (5) hide show
  1. Dockerfile +1 -1
  2. openenv.yaml +3 -3
  3. server/app.py +45 -6
  4. ui/.env.example +1 -1
  5. ui/src/App.jsx +31 -31
Dockerfile CHANGED
@@ -16,7 +16,7 @@ COPY ui/ ./
16
  # HF Spaces forwards the matching secrets as Docker build-args automatically
17
  # when you set them in Space Settings β†’ Variables and secrets.
18
  ARG VITE_HF_TOKEN=""
19
- ARG VITE_HF_MODEL="mistralai/Mistral-7B-Instruct-v0.2"
20
  ENV VITE_HF_TOKEN=${VITE_HF_TOKEN}
21
  ENV VITE_HF_MODEL=${VITE_HF_MODEL}
22
 
 
16
  # HF Spaces forwards the matching secrets as Docker build-args automatically
17
  # when you set them in Space Settings β†’ Variables and secrets.
18
  ARG VITE_HF_TOKEN=""
19
+ ARG VITE_HF_MODEL="meta-llama/Llama-3.1-8B-Instruct"
20
  ENV VITE_HF_TOKEN=${VITE_HF_TOKEN}
21
  ENV VITE_HF_MODEL=${VITE_HF_MODEL}
22
 
openenv.yaml CHANGED
@@ -26,11 +26,11 @@ env:
26
 
27
  variables:
28
  - name: HF_MODEL
29
- default: "mistralai/Mistral-7B-Instruct-v0.2"
30
  description: >
31
  Any HF chat/instruct model compatible with the [INST] prompt format.
32
- Alternatives: HuggingFaceH4/zephyr-7b-beta
33
- meta-llama/Llama-3.1-8B-Instruct (needs license accept)
34
  - name: PORT
35
  default: "7860"
36
  description: Port exposed by the Docker container (HF Spaces default).
 
26
 
27
  variables:
28
  - name: HF_MODEL
29
+ default: "meta-llama/Llama-3.1-8B-Instruct"
30
  description: >
31
  Any HF chat/instruct model compatible with the [INST] prompt format.
32
+ If you prefer a different model, use HuggingFaceH4/blackbird-7b or
33
+ meta-llama/Llama-3.1-8B-Instruct (the latter may require license acceptance).
34
  - name: PORT
35
  default: "7860"
36
  description: Port exposed by the Docker container (HF Spaces default).
server/app.py CHANGED
@@ -22,6 +22,7 @@ from fastapi.middleware.cors import CORSMiddleware
22
  from fastapi.responses import JSONResponse
23
 
24
  from server.environment import GameState, InferResponse, build_prompt
 
25
  import models
26
 
27
  # ── Logging ───────────────────────────────────────────────────────────────────
@@ -101,6 +102,9 @@ async def root():
101
  }
102
 
103
 
 
 
 
104
  @app.get("/api/config")
105
  async def config():
106
  """Public runtime configuration for the frontend."""
@@ -111,11 +115,39 @@ async def config():
111
  }
112
 
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  @app.post("/api/infer", response_model=InferResponse)
115
- async def infer(state: GameState, request: Request):
116
  """
117
- Accept a GameState JSON body, build the LLM prompt,
118
- run inference, and return the chosen action + thought.
119
 
120
  Inference priority: local pipeline β†’ HF Inference API β†’ rule fallback.
121
  """
@@ -126,10 +158,17 @@ async def infer(state: GameState, request: Request):
126
  detail="Too many requests β€” wait a few seconds.",
127
  )
128
 
129
- prompt = build_prompt(state)
 
 
 
 
 
 
 
130
  logger.info(
131
- f"[infer] gen={state.generation} ip={ip} "
132
- f"challenge={state.activeChallenge.type if state.activeChallenge else 'none'}"
133
  )
134
 
135
  # Try inference paths
 
22
  from fastapi.responses import JSONResponse
23
 
24
  from server.environment import GameState, InferResponse, build_prompt
25
+ from inference import SurvivalIslandEnvironment
26
  import models
27
 
28
  # ── Logging ───────────────────────────────────────────────────────────────────
 
102
  }
103
 
104
 
105
+ env = SurvivalIslandEnvironment()
106
+
107
+
108
  @app.get("/api/config")
109
  async def config():
110
  """Public runtime configuration for the frontend."""
 
115
  }
116
 
117
 
118
+ @app.post("/openenv/reset")
119
+ async def openenv_reset():
120
+ """Reset the OpenEnv environment and return the initial state."""
121
+ state = env.reset()
122
+ return {"state": state}
123
+
124
+
125
+ @app.post("/openenv/step")
126
+ async def openenv_step(payload: dict):
127
+ """Advance the OpenEnv environment with a given action."""
128
+ action = payload.get("action")
129
+ if not action or not isinstance(action, str):
130
+ raise HTTPException(status_code=400, detail="Missing or invalid action")
131
+ next_state, reward, done, info = env.step(action)
132
+ return {
133
+ "state": next_state,
134
+ "reward": reward,
135
+ "done": done,
136
+ "info": info,
137
+ }
138
+
139
+
140
+ @app.get("/openenv/state")
141
+ async def openenv_state():
142
+ """Return the current OpenEnv environment state."""
143
+ return {"state": env.state()}
144
+
145
+
146
  @app.post("/api/infer", response_model=InferResponse)
147
+ async def infer(request: Request):
148
  """
149
+ Accept either a raw prompt or the frontend GameState JSON body.
150
+ Build the LLM prompt server-side and return the chosen action + thought.
151
 
152
  Inference priority: local pipeline β†’ HF Inference API β†’ rule fallback.
153
  """
 
158
  detail="Too many requests β€” wait a few seconds.",
159
  )
160
 
161
+ data = await request.json()
162
+ state = None
163
+ if isinstance(data, dict) and "prompt" in data:
164
+ prompt = data["prompt"]
165
+ else:
166
+ state = GameState.model_validate(data)
167
+ prompt = build_prompt(state)
168
+
169
  logger.info(
170
+ f"[infer] model={os.getenv('HF_MODEL')} ip={ip} "
171
+ f"challenge={state.activeChallenge.type if state and state.activeChallenge else 'none'}"
172
  )
173
 
174
  # Try inference paths
ui/.env.example CHANGED
@@ -25,4 +25,4 @@ VITE_HF_TOKEN=YOUR_HF_TOKEN_HERE
25
  #
26
  # Requires license acceptance on HuggingFace.co:
27
  # meta-llama/Llama-3.1-8B-Instruct
28
- VITE_HF_MODEL=mistralai/Mistral-7B-Instruct-v0.2
 
25
  #
26
  # Requires license acceptance on HuggingFace.co:
27
  # meta-llama/Llama-3.1-8B-Instruct
28
+ VITE_HF_MODEL=meta-llama/Llama-3.1-8B-Instruct
ui/src/App.jsx CHANGED
@@ -13,10 +13,8 @@ const TICK_RATE = 1000;
13
  const WORLD_WIDTH = 6000;
14
  const WORLD_HEIGHT = 3000;
15
 
16
- // ─── HuggingFace config from .env ────────────────────────────────────────────
17
- const HF_TOKEN = import.meta.env.VITE_HF_TOKEN;
18
- const HF_MODEL = import.meta.env.VITE_HF_MODEL;
19
- // ─────────────────────────────────────────────────────────────────────────────
20
 
21
  const ZONES = {
22
  OCEAN: { baseEndX: 400, color1: '#094b65', color2: '#20a4c0' },
@@ -243,11 +241,30 @@ export default function App() {
243
 
244
  const [hudState, setHudState] = useState(null);
245
  const [showMemoryLog, setShowMemoryLog] = useState(false);
 
246
  const canvasRef = useRef(null);
247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
248
  const addLog = (message, type = 'info') => {
249
  gameRef.current.logs = [{ id: Date.now(), message, type, time: Date.now() }];
250
- };
251
 
252
  const startGame = (mode) => {
253
  WORLD_OBJECTS = generateWorld();
@@ -473,7 +490,7 @@ export default function App() {
473
  if (s.player.speedMult < 1) s.player.speedMult = Math.min(1, s.player.speedMult + 0.01);
474
  };
475
 
476
- // ─── HUGGING FACE API CALL (replaces Anthropic callClaudeAPI) ────────────
477
  const callClaudeAPI = async (s) => {
478
  s.ai.llmThinking = true;
479
  const isNight = s.time < 6 || s.time > 18;
@@ -512,25 +529,13 @@ Valid Actions: FORAGE, HUNT, FISH, GET_WATER, SEEK_SHELTER, BUILD_CAMP, UPGRADE_
512
  Respond ONLY with a raw JSON object β€” no markdown, no extra text. Example: {"action":"FORAGE","thought":"Need wood and resources"} [/INST]`;
513
 
514
  try {
515
- const response = await fetch(
516
- `https://api-inference.huggingface.co/models/${HF_MODEL}`,
517
- {
518
- method: 'POST',
519
- headers: {
520
- 'Authorization': `Bearer ${HF_TOKEN}`,
521
- 'Content-Type': 'application/json',
522
- },
523
- body: JSON.stringify({
524
- inputs: prompt,
525
- parameters: {
526
- max_new_tokens: 80,
527
- temperature: 0.7,
528
- return_full_text: false,
529
- stop: ['\n\n', '</s>', '[INST]'],
530
- },
531
- }),
532
- }
533
- );
534
 
535
  if (!response.ok) {
536
  const err = await response.json().catch(() => ({}));
@@ -1173,12 +1178,7 @@ Respond ONLY with a raw JSON object β€” no markdown, no extra text. Example: {"a
1173
  <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-blue-900/20 via-zinc-950 to-zinc-950"></div>
1174
  <div className="max-w-3xl w-full space-y-6 z-10">
1175
  <h1 className="text-5xl font-black tracking-tighter text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-emerald-400 border-b border-zinc-800/50 pb-6 flex items-center gap-4"><Brain className="text-blue-500" size={48}/> EVOLUTIONARY AI</h1>
1176
- <p className="leading-relaxed text-zinc-400 text-lg">Initialize Subject-01 into the high-fidelity 2.5D simulation. Powered by HuggingFace AI ({HF_MODEL || 'model not set'}) with persistent memory across 6 generations.</p>
1177
- {(!HF_TOKEN || !HF_MODEL) && (
1178
- <div className="bg-red-950/60 border border-red-700 rounded-xl px-5 py-3 text-red-300 text-sm font-mono">
1179
- ⚠ Missing env vars: {!HF_TOKEN ? 'VITE_HF_TOKEN ' : ''}{!HF_MODEL ? 'VITE_HF_MODEL' : ''}. AI will run in offline fallback mode.
1180
- </div>
1181
- )}
1182
  <div className="grid grid-cols-2 gap-6 mt-8">
1183
  <button onClick={() => startGame('hardcore')} className="bg-zinc-900/80 backdrop-blur border border-red-900/50 p-8 rounded-xl hover:bg-red-950/40 transition-all text-left flex flex-col gap-3 group shadow-2xl">
1184
  <div className="flex items-center gap-3 text-red-500 font-bold text-2xl"><Skull size={28}/> HARDCORE MODE</div>
 
13
  const WORLD_WIDTH = 6000;
14
  const WORLD_HEIGHT = 3000;
15
 
16
+ // ─── Runtime backend config ─────────────────────────────────────────────────
17
+ // Token is kept server-side; the frontend calls /api/infer and reads /api/config.
 
 
18
 
19
  const ZONES = {
20
  OCEAN: { baseEndX: 400, color1: '#094b65', color2: '#20a4c0' },
 
241
 
242
  const [hudState, setHudState] = useState(null);
243
  const [showMemoryLog, setShowMemoryLog] = useState(false);
244
+ const [brainConfig, setBrainConfig] = useState({ model: 'loading...', hasToken: false, localPipeline: false });
245
  const canvasRef = useRef(null);
246
 
247
+ useEffect(() => {
248
+ const fetchConfig = async () => {
249
+ try {
250
+ const response = await fetch('/api/config');
251
+ if (!response.ok) return;
252
+ const data = await response.json();
253
+ setBrainConfig({
254
+ model: data.model || 'unknown',
255
+ hasToken: Boolean(data.hasToken),
256
+ localPipeline: Boolean(data.localPipeline),
257
+ });
258
+ } catch (error) {
259
+ console.warn('Failed to load backend config:', error);
260
+ }
261
+ };
262
+ fetchConfig();
263
+ }, []);
264
+
265
  const addLog = (message, type = 'info') => {
266
  gameRef.current.logs = [{ id: Date.now(), message, type, time: Date.now() }];
267
+ };
268
 
269
  const startGame = (mode) => {
270
  WORLD_OBJECTS = generateWorld();
 
490
  if (s.player.speedMult < 1) s.player.speedMult = Math.min(1, s.player.speedMult + 0.01);
491
  };
492
 
493
+ // ─── BACKEND INFERENCE CALL (via /api/infer) ─────────────────────────────
494
  const callClaudeAPI = async (s) => {
495
  s.ai.llmThinking = true;
496
  const isNight = s.time < 6 || s.time > 18;
 
529
  Respond ONLY with a raw JSON object β€” no markdown, no extra text. Example: {"action":"FORAGE","thought":"Need wood and resources"} [/INST]`;
530
 
531
  try {
532
+ const response = await fetch('/api/infer', {
533
+ method: 'POST',
534
+ headers: {
535
+ 'Content-Type': 'application/json',
536
+ },
537
+ body: JSON.stringify({ prompt }),
538
+ });
 
 
 
 
 
 
 
 
 
 
 
 
539
 
540
  if (!response.ok) {
541
  const err = await response.json().catch(() => ({}));
 
1178
  <div className="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-blue-900/20 via-zinc-950 to-zinc-950"></div>
1179
  <div className="max-w-3xl w-full space-y-6 z-10">
1180
  <h1 className="text-5xl font-black tracking-tighter text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-emerald-400 border-b border-zinc-800/50 pb-6 flex items-center gap-4"><Brain className="text-blue-500" size={48}/> EVOLUTIONARY AI</h1>
1181
+ <p className="leading-relaxed text-zinc-400 text-lg">Initialize Subject-01 into the high-fidelity 2.5D simulation. Powered by HuggingFace AI with persistent memory across 6 generations.</p>
 
 
 
 
 
1182
  <div className="grid grid-cols-2 gap-6 mt-8">
1183
  <button onClick={() => startGame('hardcore')} className="bg-zinc-900/80 backdrop-blur border border-red-900/50 p-8 rounded-xl hover:bg-red-950/40 transition-all text-left flex flex-col gap-3 group shadow-2xl">
1184
  <div className="flex items-center gap-3 text-red-500 font-bold text-2xl"><Skull size={28}/> HARDCORE MODE</div>