ThongCoding commited on
Commit
5fbd2ea
·
1 Parent(s): c989e45
Files changed (4) hide show
  1. .gitignore +2 -1
  2. app.py +59 -35
  3. structure_list.json +32 -0
  4. utils.py +32 -0
.gitignore CHANGED
@@ -1,2 +1,3 @@
1
  tester.py
2
- __pycache__/
 
 
1
  tester.py
2
+ __pycache__/
3
+ .vscode/
app.py CHANGED
@@ -1,12 +1,13 @@
 
 
 
1
  import curl_cffi
2
  from fastapi import FastAPI
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
5
- import os
6
-
7
  import uvicorn
8
 
9
- # Load Gemini API key from secret
10
  GEM_API_KEY = os.getenv('GEMINI_API_KEY')
11
 
12
  # Init app
@@ -15,67 +16,90 @@ app = FastAPI()
15
  # CORS (allow frontend)
16
  app.add_middleware(
17
  CORSMiddleware,
18
- allow_origins=["*"], # Optional: restrict in prod
19
  allow_methods=["*"],
20
  allow_headers=["*"],
21
  )
22
 
23
- # Request model
24
  class PromptRequest(BaseModel):
25
- structure_name: str
26
- structure_desc: str
27
- blocks_allowed: list[str] # Enforce lowercase block names client-side
28
 
29
- @app.post("/generate_structure")
30
- async def generate_with_gemini(req: PromptRequest):
31
- try:
32
- # Compose the extremely strict prompt
33
- prompt = f"""You are a Minecraft-style structure planner. You have the curiosity to build almost anything you could think of. {req.structure_name}
34
 
35
- {req.structure_desc}
 
 
 
 
36
 
37
- Only output a JSON object describing a 3D structure using this format:
 
 
38
 
39
  {{
40
  "width": <int>,
41
  "height": <int>,
42
- "depth": <int>,
43
- "layers": [
44
- [
45
- ["stone", "stone", "stone"],
46
- ["stone", "air", "stone"],
47
- ["stone", "stone", "stone"]
48
- ],
49
- ...
50
  ]
51
  }}
52
 
53
  Only use lowercase Minecraft block IDs (e.g. "stone", "air", "glass", "planks").
54
- You could only build this structure using {" ".join(req.blocks_allowed)}
55
  Do not include any natural language or explanation.
56
  Output strictly valid JSON only."""
57
 
58
- # Call Gemini API using curl_cffi
59
- session = curl_cffi.Session(impersonate="chrome110") # Optional impersonation
60
- response = session.post(
 
 
61
  url="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent",
62
  headers={
63
  "x-goog-api-key": GEM_API_KEY,
64
  "Content-Type": "application/json"
65
  },
66
  json={
67
- "contents": [{
68
- "parts": [{
69
- "text": prompt
70
- }]
71
- }]
72
  }
73
  )
74
-
75
  return {"response": response.text}
 
 
 
 
 
 
 
 
 
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  except Exception as e:
78
  return {"error": str(e)}
79
 
80
- if __name__ == "__main__":
81
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
1
+ import os
2
+ import json
3
+ import random
4
  import curl_cffi
5
  from fastapi import FastAPI
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from pydantic import BaseModel
 
 
8
  import uvicorn
9
 
10
+ # Load Gemini API key from environment
11
  GEM_API_KEY = os.getenv('GEMINI_API_KEY')
12
 
13
  # Init app
 
16
  # CORS (allow frontend)
17
  app.add_middleware(
18
  CORSMiddleware,
19
+ allow_origins=["*"],
20
  allow_methods=["*"],
21
  allow_headers=["*"],
22
  )
23
 
24
+ # Pydantic model for manual prompting
25
  class PromptRequest(BaseModel):
26
+ prompt: str
 
 
27
 
28
+ # Utility to pick a random structure and build a strict prompt
29
+ def pick_random_structure():
30
+ with open("structure_list.json", "r") as f:
31
+ return random.choice(json.load(f))
 
32
 
33
+ def make_prompt(structure):
34
+ name = structure["name"]
35
+ desc = structure["description"]
36
+ blocks = " ".join(structure["blocks_allowed"])
37
+ return f"""You are a Minecraft-style structure planner. You have the curiosity to build almost anything you could think of. {name}
38
 
39
+ Structure description: {desc}
40
+
41
+ Only output a JSON object describing a 2D structure using this format:
42
 
43
  {{
44
  "width": <int>,
45
  "height": <int>,
46
+ "layout": [
47
+ ["stone", "air", "stone"],
48
+ ["stone", "air", "stone"],
49
+ ["stone", "stone", "stone"]
 
 
 
 
50
  ]
51
  }}
52
 
53
  Only use lowercase Minecraft block IDs (e.g. "stone", "air", "glass", "planks").
54
+ You could only build this structure using {blocks}
55
  Do not include any natural language or explanation.
56
  Output strictly valid JSON only."""
57
 
58
+ # Manual prompting route
59
+ @app.post("/generate_structure")
60
+ async def generate_with_gemini(req: PromptRequest):
61
+ try:
62
+ response = curl_cffi.Session().post(
63
  url="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent",
64
  headers={
65
  "x-goog-api-key": GEM_API_KEY,
66
  "Content-Type": "application/json"
67
  },
68
  json={
69
+ "contents": [
70
+ {
71
+ "parts": [{"text": req.prompt}]
72
+ }
73
+ ]
74
  }
75
  )
 
76
  return {"response": response.text}
77
+ except Exception as e:
78
+ return {"error": str(e)}
79
+
80
+ # Auto structure generation route
81
+ @app.get("/generate_random_structure")
82
+ async def generate_random_structure():
83
+ try:
84
+ structure = pick_random_structure()
85
+ prompt = make_prompt(structure)
86
 
87
+ response = curl_cffi.Session().post(
88
+ url="https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent",
89
+ headers={
90
+ "x-goog-api-key": GEM_API_KEY,
91
+ "Content-Type": "application/json"
92
+ },
93
+ json={
94
+ "contents": [
95
+ {
96
+ "parts": [{"text": prompt}]
97
+ }
98
+ ]
99
+ }
100
+ )
101
+ return response.json()
102
  except Exception as e:
103
  return {"error": str(e)}
104
 
105
+ if __name__ == "__main__": uvicorn.run(app, host='localhost')
 
structure_list.json ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "name": "Tiny Watchtower",
4
+ "description": "A small elevated lookout tower made from stone and planks.",
5
+ "blocks_allowed": ["stone", "planks", "air"],
6
+ "layout": [
7
+ ["stone", "stone", "stone"],
8
+ ["planks", "air", "planks"],
9
+ ["stone", "stone", "stone"]
10
+ ]
11
+ },
12
+ {
13
+ "name": "Glass Hut",
14
+ "description": "A compact shelter made primarily from glass with a stone base.",
15
+ "blocks_allowed": ["glass", "stone", "air"],
16
+ "layout": [
17
+ ["glass", "glass", "glass"],
18
+ ["glass", "air", "glass"],
19
+ ["stone", "stone", "stone"]
20
+ ]
21
+ },
22
+ {
23
+ "name": "Lantern Post",
24
+ "description": "A single vertical pillar with a torch at the top.",
25
+ "blocks_allowed": ["stone", "torch"],
26
+ "layout": [
27
+ ["torch"],
28
+ ["stone"],
29
+ ["stone"]
30
+ ]
31
+ }
32
+ ]
utils.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import random
3
+
4
+ def pick_random_structure():
5
+ with open("structure_list.json", "r") as f:
6
+ return random.choice(json.load(f))
7
+
8
+ def make_prompt(structure):
9
+ name = structure["name"]
10
+ desc = structure["description"]
11
+ blocks = " ".join(structure["blocks_allowed"])
12
+
13
+ return f"""You are a Minecraft-style structure planner. You have the curiosity to build almost anything you could think of. {name}
14
+
15
+ Structure description: {desc}
16
+
17
+ Only output a JSON object describing a 2D structure using this format:
18
+
19
+ {{
20
+ "width": <int>,
21
+ "height": <int>,
22
+ "layout": [
23
+ ["stone", "air", "stone"],
24
+ ["stone", "air", "stone"],
25
+ ["stone", "stone", "stone"]
26
+ ]
27
+ }}
28
+
29
+ Only use lowercase Minecraft block IDs (e.g. "stone", "air", "glass", "planks").
30
+ You could only build this structure using {blocks}
31
+ Do not include any natural language or explanation.
32
+ Output strictly valid JSON only."""