Spaces:
Sleeping
Sleeping
File size: 5,323 Bytes
6343479 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | """LLM World Planner for FlyBrain V8/V9.
Implements Sections 46, 50, 90:
- Translates natural language prompts into declarative structured WorldIntent.
- Resolves assets via AssetResolver and coordinates spatial placement.
- Offline-first: uses local Qwen LLM when available, falls back to deterministic semantic parser.
"""
import os
import json
import re
from typing import Dict, Any, List, Optional
from src.assets.compiler.types import AssetClass
from src.assets.resolver import AssetResolver
class WorldPlan:
def __init__(self, raw_prompt: str, biome: str, structures: List[Dict[str, Any]],
water: Optional[Dict[str, Any]] = None, resolved_assets: Optional[List[Dict[str, Any]]] = None):
self.raw_prompt = raw_prompt
self.biome = biome
self.structures = structures
self.water = water
self.resolved_assets = resolved_assets or []
def to_dict(self) -> Dict[str, Any]:
return {
"raw_prompt": self.raw_prompt,
"biome": self.biome,
"structures": self.structures,
"water": self.water,
"resolved_assets": self.resolved_assets
}
class WorldPlanner:
def __init__(self, resolver: Optional[AssetResolver] = None):
self.resolver = resolver or AssetResolver()
def _extract_intent_llm(self, prompt: str) -> Optional[Dict[str, Any]]:
try:
from src.models.manager import get_model_manager
mgr = get_model_manager()
if not mgr.is_available("llm"):
return None
llm = mgr.get_llm()
sys_msg = (
"You are the FlyBrain World Planner. Output valid JSON only with keys: "
"'biome', 'structures' (list of {type, material, span_m, width_m, x, y, z}), 'water'."
)
resp = llm.create_chat_completion(
messages=[
{"role": "system", "content": sys_msg},
{"role": "user", "content": prompt}
],
max_tokens=256,
temperature=0.1
)
text = resp["choices"][0]["message"]["content"]
# Extract JSON block
m = re.search(r"\{.*\}", text, re.DOTALL)
if m:
return json.loads(m.group(0))
except Exception:
pass
return None
def _extract_intent_rule_based(self, prompt: str) -> Dict[str, Any]:
p = prompt.lower()
biome = "temperate_valley"
if "pine" in p or "forest" in p:
biome = "pine_forest"
elif "mountain" in p or "rock" in p or "highland" in p:
biome = "rocky_highlands"
structures = []
water = None
if "bridge" in p:
structures.append({
"type": "wooden_bridge",
"material": "wood",
"span_m": 4.0,
"width_m": 1.5,
"x": 2.0,
"y": 5.0,
"z": 0.0
})
water = {"type": "stream", "x": 2.0, "y": 5.0, "width": 2.5}
if "tower" in p or "watchtower" in p:
structures.append({
"type": "watchtower",
"material": "wood",
"sx": 2.5,
"sy": 2.5,
"sz": 6.0,
"x": -5.0,
"y": 6.0,
"z": 0.0
})
if "cabin" in p or "house" in p or "settlement" in p:
structures.append({
"type": "log_cabin",
"material": "wood",
"sx": 4.0,
"sy": 4.0,
"sz": 2.8,
"x": -6.0,
"y": 8.0,
"z": 0.0
})
if not structures:
structures.append({
"type": "marker_structure",
"material": "stone",
"sx": 1.0,
"sy": 1.0,
"sz": 1.0,
"x": 0.0,
"y": 6.0,
"z": 0.0
})
return {"biome": biome, "structures": structures, "water": water}
def plan_world(self, prompt: str, seed: int = 42) -> WorldPlan:
"""Section 46 & 90: Generates structured declarative plan and resolves assets."""
intent = self._extract_intent_llm(prompt)
if not intent or "structures" not in intent:
intent = self._extract_intent_rule_based(prompt)
biome = intent.get("biome", "temperate_valley")
raw_structs = intent.get("structures", [])
water = intent.get("water")
resolved = []
for s in raw_structs:
stype = s.get("type", "structure")
params = {k: v for k, v in s.items() if k not in ("type", "x", "y", "z")}
res = self.resolver.resolve_asset(
semantic_name=stype,
category=AssetClass.STRUCTURE,
params=params,
seed=seed
)
resolved.append({
**s,
"resolution": res
})
return WorldPlan(
raw_prompt=prompt,
biome=biome,
structures=raw_structs,
water=water,
resolved_assets=resolved
)
|