Spaces:
Runtime error
Runtime error
File size: 7,695 Bytes
4f64da5 f4af987 4f64da5 6896326 9802882 6896326 4f64da5 f4af987 6896326 9802882 6896326 9802882 6896326 9802882 6896326 9802882 6896326 9802882 6896326 9802882 6896326 9802882 6896326 9802882 4f64da5 6896326 9802882 6896326 9802882 4f64da5 9802882 4f64da5 6896326 4f64da5 6896326 f4af987 4f64da5 6896326 4f64da5 6896326 f4af987 6896326 4f64da5 6896326 4f64da5 |
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 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
"use client"
import { useEffect, useRef, useState, useTransition } from "react"
import { ImageRenderer } from "@/components/business/image-renderer"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { render } from "./render"
import { RenderedScene } from "./types"
import { predict } from "./predict"
import { GameType } from "./games/types"
import { defaultGame, games, getGame } from "./games"
export default function Main() {
const [isPending, startTransition] = useTransition()
const [rendered, setRendered] = useState<RenderedScene>({
assetUrl: "",
error: "",
maskBase64: "",
segments:[]
})
const ref = useRef<GameType>(defaultGame)
const [situation, setSituation] = useState("")
const [scene, setScene] = useState("")
const [dialogue, setDialogue] = useState("")
const [hoveredActionnable, setHoveredActionnable] = useState("")
const [isLoading, setLoading] = useState(true)
const loadNextScene = async (nextSituation?: string, nextActionnables?: string[]) => {
// console.log(`update view..`)
setLoading(true)
await startTransition(async () => {
// console.log(`getting agent..`)
// note: we use a ref so that it can be changed in the background
const type = ref?.current
const game = getGame(type)
// console.log(`rendering scene..`)
const newRendered = await render(
// SCENE PROMPT
[...game.getScenePrompt(nextSituation)].join(", "),
// ACTIONNABLES
(Array.isArray(nextActionnables) && nextActionnables.length
? nextActionnables
: game.initialActionnables
).slice(0, 6) // too many can slow us down it seems
)
// detect if something changed in the background
if (type !== ref?.current) {
console.log("agent type changed! reloading scene")
setTimeout(() => { loadNextScene() }, 0)
return
}
if (newRendered.assetUrl) {
// console.log(`got a new url: ${newRendered.assetUrl}`)
setScene(scene)
setRendered(newRendered)
setLoading(false)
}
})
}
useEffect(() => {
loadNextScene()
}, [])
const handleUserAction = async (actionnable: string) => {
console.log("user actionnable:", actionnable)
// TODO: ask Llama2 what to do about it
// we need a frame and some actionnables,
// perhaps even some music or sound effects
await startTransition(async () => {
setLoading(true)
const game = getGame(ref.current)
const initialPrompt = [...game.getScenePrompt()].join(", ")
const currentPrompt = situation
? [...game.getScenePrompt(situation)].join(", ")
: initialPrompt
try {
const basePrompt = [
`QUESTION: You are the AI game master of a role video game.`,
initialPrompt !== currentPrompt ? `The initial scene of the game was this: "${initialPrompt}".` : '',
`The player is currently in this scene: "${currentPrompt}".`,
`The player has just clicked on "${actionnable}".`
]
console.log("ask the LLM to invent next steps..")
const rawSituation = await predict([
...basePrompt,
`Please describe the new scene to display in intricate details: the environment, lights, era, characters, objects, textures, light etc. You must include important objects, that the user can click on (eg. characters, doors, vehicles, useful objects).\nANSWER:`
].join(" "))
console.log(`rawSituation: `, rawSituation)
if (!rawSituation) {
throw new Error("failed to generate the situation")
}
const newSituation = `${rawSituation.split("QUESTION:")[0] || ""}`
if (!newSituation) {
throw new Error("failed to parse the situation")
}
console.log(`newSituation: `, newSituation)
const rawActionnables = await predict([
...basePrompt,
`Here are the 4 most important objects visible in this scene, that the user can click on. The list is in JSON (list of strings). You must list basic name of things (eg. "parrot", "chest", "spaceship", "glass", "door", "person", "window", "light", "knob", "button" etc..) \nJSON = [`
].join(" "))
console.log(`rawActionnables: `, rawActionnables)
if (!rawActionnables) {
throw new Error("failed to generate the actionnables")
}
let newActionnables = []
try {
newActionnables = (JSON.parse(
`[${rawActionnables.split("]")[0] || ""}]`
) as string[]).map(item =>
// clean the words to remove any punctuation
item.replace(/\W/g, '').trim()
)
if (!newActionnables.length) {
throw new Error("no actionnables")
}
} catch (err) {
throw new Error("failed to parse the actionnables")
}
console.log(`newActionnables: `, newActionnables)
const rawDialogue = await predict([
...basePrompt,
`As a game master, what should you say next? (Only reply with 2 sentences, please).\nANSWER:`
].join(" "))
console.log(`rawDialogue: `, rawDialogue)
if (!rawDialogue) {
throw new Error("failed to generate the dialogue")
}
const newDialogue = `${rawDialogue.split("QUESTION:")[0] || ""}`
if (!newDialogue) {
throw new Error("failed to parse the dialogue")
}
console.log(`newDialogue: `, newDialogue)
setDialogue(newDialogue)
setSituation(newSituation)
console.log("loading next scene..")
await loadNextScene(newSituation, newActionnables)
// todo we could also use useEffect
} catch (err) {
console.error(err)
}
})
}
return (
<div className="flex flex-col w-full pt-4">
<div className="flex flex-col space-y-3 px-2">
<div className="flex flex-row items-center space-x-3">
<label className="flex">Select a story:</label>
<Select
defaultValue={defaultGame}
onValueChange={(value) => {
ref.current = value as GameType
setRendered({
assetUrl: "",
error: "",
maskBase64: "",
segments:[]
})
}}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Type" />
</SelectTrigger>
<SelectContent>
{Object.entries(games).map(([key, game]) =>
<SelectItem key={key} value={key}>{game.title}</SelectItem>
)}
</SelectContent>
</Select>
</div>
<p className="text-xl">The server is blowing up! Loading a panel may take a few minutes.</p>
<p className="text-xl">{dialogue}</p>
<div className="flex flex-row">
<div className="text-xl mr-2">🔎 Possible items:</div>
{rendered.segments.map((segment, i) =>
<div key={i} className="flex flex-row text-xl mr-2">
<div className="">{segment.label}</div>
{i < (rendered.segments.length - 1) ? <div>,</div> : null}
</div>)}
</div>
<p className="text-xl font-normal">You may be looking at.. <span className="font-bold">{hoveredActionnable || "nothing"}</span></p>
</div>
<ImageRenderer
rendered={rendered}
onUserAction={handleUserAction}
onUserHover={setHoveredActionnable}
isLoading={isLoading}
/>
</div>
)
} |