Spaces:
Runtime error
Runtime error
Commit
•
9802882
1
Parent(s):
9a8adca
working to add some llama2 step
Browse files- package-lock.json +9 -0
- package.json +1 -0
- src/app/agents/pirates.ts +6 -4
- src/app/agents/server.ts +10 -0
- src/app/main.tsx +50 -45
- src/components/business/image-renderer.tsx +142 -58
package-lock.json
CHANGED
@@ -8,6 +8,7 @@
|
|
8 |
"name": "video-quest",
|
9 |
"version": "0.0.0",
|
10 |
"dependencies": {
|
|
|
11 |
"@radix-ui/react-accordion": "^1.1.2",
|
12 |
"@radix-ui/react-avatar": "^1.0.3",
|
13 |
"@radix-ui/react-checkbox": "^1.0.4",
|
@@ -183,6 +184,14 @@
|
|
183 |
"react-dom": ">=16.8.0"
|
184 |
}
|
185 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
"node_modules/@humanwhocodes/config-array": {
|
187 |
"version": "0.11.10",
|
188 |
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
|
|
|
8 |
"name": "video-quest",
|
9 |
"version": "0.0.0",
|
10 |
"dependencies": {
|
11 |
+
"@huggingface/inference": "^2.6.1",
|
12 |
"@radix-ui/react-accordion": "^1.1.2",
|
13 |
"@radix-ui/react-avatar": "^1.0.3",
|
14 |
"@radix-ui/react-checkbox": "^1.0.4",
|
|
|
184 |
"react-dom": ">=16.8.0"
|
185 |
}
|
186 |
},
|
187 |
+
"node_modules/@huggingface/inference": {
|
188 |
+
"version": "2.6.1",
|
189 |
+
"resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-2.6.1.tgz",
|
190 |
+
"integrity": "sha512-qFYchgOCPeEkZJKiSr7Kz62QwukJtgkeQCT7Q0SSKUcvHpTQVNJp6i/JrJMR4dBdzQysJ1SZDC0pLBBnnskTag==",
|
191 |
+
"engines": {
|
192 |
+
"node": ">=18"
|
193 |
+
}
|
194 |
+
},
|
195 |
"node_modules/@humanwhocodes/config-array": {
|
196 |
"version": "0.11.10",
|
197 |
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
|
package.json
CHANGED
@@ -9,6 +9,7 @@
|
|
9 |
"lint": "next lint"
|
10 |
},
|
11 |
"dependencies": {
|
|
|
12 |
"@radix-ui/react-accordion": "^1.1.2",
|
13 |
"@radix-ui/react-avatar": "^1.0.3",
|
14 |
"@radix-ui/react-checkbox": "^1.0.4",
|
|
|
9 |
"lint": "next lint"
|
10 |
},
|
11 |
"dependencies": {
|
12 |
+
"@huggingface/inference": "^2.6.1",
|
13 |
"@radix-ui/react-accordion": "^1.1.2",
|
14 |
"@radix-ui/react-avatar": "^1.0.3",
|
15 |
"@radix-ui/react-checkbox": "^1.0.4",
|
src/app/agents/pirates.ts
CHANGED
@@ -34,10 +34,13 @@ const actionnables = [
|
|
34 |
// "ship's wheel",
|
35 |
// "hat",
|
36 |
// "barrel",
|
37 |
-
|
38 |
// "rope",
|
39 |
// "bucket",
|
|
|
40 |
"parrot",
|
|
|
|
|
41 |
// "wooden leg"
|
42 |
]
|
43 |
|
@@ -54,9 +57,8 @@ export const agent: Agent = {
|
|
54 |
const prompt = [
|
55 |
`screenshot from an adventure videogame`,
|
56 |
`inside the hold of a pirate ship`,
|
57 |
-
`
|
58 |
-
`a parrot`,
|
59 |
-
`and a wooden leg`,
|
60 |
`at sunset`,
|
61 |
`unreal engine`,
|
62 |
].join(", ")
|
|
|
34 |
// "ship's wheel",
|
35 |
// "hat",
|
36 |
// "barrel",
|
37 |
+
"cannon",
|
38 |
// "rope",
|
39 |
// "bucket",
|
40 |
+
"skull",
|
41 |
"parrot",
|
42 |
+
"lock",
|
43 |
+
"ship",
|
44 |
// "wooden leg"
|
45 |
]
|
46 |
|
|
|
57 |
const prompt = [
|
58 |
`screenshot from an adventure videogame`,
|
59 |
`inside the hold of a pirate ship`,
|
60 |
+
`a pirate chest in the center with a large lock`,
|
61 |
+
`a parrot on top of it`,
|
|
|
62 |
`at sunset`,
|
63 |
`unreal engine`,
|
64 |
].join(", ")
|
src/app/agents/server.ts
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use server"
|
2 |
+
|
3 |
+
import { HfInference } from "@huggingface/inference"
|
4 |
+
|
5 |
+
const hfi = new HfInference(process.env.HF_API_TOKEN)
|
6 |
+
const hf = hfi.endpoint(`${process.env.HF_INFERENCE_ENDPOINT_URL || ""}`)
|
7 |
+
|
8 |
+
export async function decideNextSteps(userAction: string) {
|
9 |
+
return ""
|
10 |
+
}
|
src/app/main.tsx
CHANGED
@@ -13,9 +13,9 @@ import {
|
|
13 |
} from "@/components/ui/select"
|
14 |
|
15 |
import { render } from "./render"
|
16 |
-
import {
|
17 |
import { agents, defaultAgent, getAgent } from "./agents"
|
18 |
-
import {
|
19 |
|
20 |
export default function Main() {
|
21 |
const [isPending, startTransition] = useTransition()
|
@@ -28,54 +28,59 @@ export default function Main() {
|
|
28 |
})
|
29 |
const ref = useRef<AgentType>(defaultAgent)
|
30 |
|
31 |
-
useEffect(() => {
|
32 |
-
|
33 |
-
const updateView = async () => {
|
34 |
-
// console.log(`update view..`)
|
35 |
-
|
36 |
-
await startTransition(async () => {
|
37 |
-
|
38 |
-
// console.log(`getting agent..`)
|
39 |
-
const type = ref?.current
|
40 |
-
const agent = getAgent(type)
|
41 |
-
|
42 |
-
// console.log(`asking agent to determine things..`)
|
43 |
-
const scene = agent.simulate()
|
44 |
-
|
45 |
-
// console.log(`rendering scene..`)
|
46 |
-
const newRendered = await render(
|
47 |
-
scene.prompt,
|
48 |
-
scene.actionnables.slice(0, 5) // too many can slow us down it seems
|
49 |
-
)
|
50 |
-
|
51 |
-
if (type !== ref?.current) {
|
52 |
-
console.log("agent type changed! reloading scene")
|
53 |
-
setTimeout(() => { updateView() }, 0)
|
54 |
-
return
|
55 |
-
}
|
56 |
-
|
57 |
-
|
58 |
-
if (newRendered.assetUrl) {
|
59 |
-
setRendered(newRendered)
|
60 |
-
// console.log(`got a new url: ${newUrl}`)
|
61 |
-
setScene(scene)
|
62 |
-
setTimeout(() => { updateView()}, 1000)
|
63 |
-
} else {
|
64 |
-
// console.log(`going to wait a bit more: ${newUrl}`)
|
65 |
-
setTimeout(() => { updateView()}, newRendered.error ? 6000 : 3000)
|
66 |
-
}
|
67 |
-
})
|
68 |
-
}
|
69 |
-
|
70 |
-
updateView()
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
}, [])
|
73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
return (
|
75 |
<div className="flex flex-col w-full pt-4">
|
76 |
<div className="flex flex-col space-y-3 px-2">
|
77 |
<div className="flex flex-row items-center space-x-3">
|
78 |
-
<label className="flex">Select a
|
79 |
<Select
|
80 |
defaultValue={defaultAgent}
|
81 |
onValueChange={(value) => {
|
@@ -97,7 +102,7 @@ export default function Main() {
|
|
97 |
</SelectContent>
|
98 |
</Select>
|
99 |
</div>
|
100 |
-
<p>Note:
|
101 |
|
102 |
{(scene) ? <div>
|
103 |
<p>Action: {scene.action}</p>
|
@@ -111,7 +116,7 @@ export default function Main() {
|
|
111 |
</div>)}
|
112 |
</div>
|
113 |
</div>
|
114 |
-
<ImageRenderer {
|
115 |
</div>
|
116 |
)
|
117 |
}
|
|
|
13 |
} from "@/components/ui/select"
|
14 |
|
15 |
import { render } from "./render"
|
16 |
+
import { AgentType, Scene } from "./agents/types"
|
17 |
import { agents, defaultAgent, getAgent } from "./agents"
|
18 |
+
import { RenderedScene } from "./types"
|
19 |
|
20 |
export default function Main() {
|
21 |
const [isPending, startTransition] = useTransition()
|
|
|
28 |
})
|
29 |
const ref = useRef<AgentType>(defaultAgent)
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
|
32 |
+
const loadNextScene = async () => {
|
33 |
+
// console.log(`update view..`)
|
34 |
+
|
35 |
+
await startTransition(async () => {
|
36 |
+
|
37 |
+
// console.log(`getting agent..`)
|
38 |
+
const type = ref?.current
|
39 |
+
const agent = getAgent(type)
|
40 |
+
|
41 |
+
// console.log(`asking agent to determine things..`)
|
42 |
+
const scene = agent.simulate()
|
43 |
+
|
44 |
+
// console.log(`rendering scene..`)
|
45 |
+
const newRendered = await render(
|
46 |
+
scene.prompt,
|
47 |
+
scene.actionnables.slice(0, 5) // too many can slow us down it seems
|
48 |
+
)
|
49 |
+
|
50 |
+
if (type !== ref?.current) {
|
51 |
+
console.log("agent type changed! reloading scene")
|
52 |
+
setTimeout(() => { loadNextScene() }, 0)
|
53 |
+
return
|
54 |
+
}
|
55 |
+
|
56 |
+
if (newRendered.assetUrl) {
|
57 |
+
setRendered(newRendered)
|
58 |
+
// console.log(`got a new url: ${newUrl}`)
|
59 |
+
setScene(scene)
|
60 |
+
}
|
61 |
+
})
|
62 |
+
}
|
63 |
+
|
64 |
+
useEffect(() => {
|
65 |
+
loadNextScene()
|
66 |
}, [])
|
67 |
|
68 |
+
const handleUserAction = (action: string) => {
|
69 |
+
console.log("user action:", action)
|
70 |
+
|
71 |
+
// TODO: ask Llama2 what to do about it
|
72 |
+
// we need a frame and some actionnables,
|
73 |
+
// perhaps even some music or sound effects
|
74 |
+
|
75 |
+
console.log("we don't know what to do, so we just load the next frame!")
|
76 |
+
loadNextScene()
|
77 |
+
}
|
78 |
+
|
79 |
return (
|
80 |
<div className="flex flex-col w-full pt-4">
|
81 |
<div className="flex flex-col space-y-3 px-2">
|
82 |
<div className="flex flex-row items-center space-x-3">
|
83 |
+
<label className="flex">Select a story:</label>
|
84 |
<Select
|
85 |
defaultValue={defaultAgent}
|
86 |
onValueChange={(value) => {
|
|
|
102 |
</SelectContent>
|
103 |
</Select>
|
104 |
</div>
|
105 |
+
<p>Note: it takes about 1 minute to generate a new game panel</p>
|
106 |
|
107 |
{(scene) ? <div>
|
108 |
<p>Action: {scene.action}</p>
|
|
|
116 |
</div>)}
|
117 |
</div>
|
118 |
</div>
|
119 |
+
<ImageRenderer rendered={rendered} onUserAction={handleUserAction} />
|
120 |
</div>
|
121 |
)
|
122 |
}
|
src/components/business/image-renderer.tsx
CHANGED
@@ -1,73 +1,115 @@
|
|
1 |
-
import { useRef } from "react"
|
2 |
|
3 |
import { ImageSegment, RenderedScene } from "@/app/types"
|
4 |
|
5 |
export const ImageRenderer = ({
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
const imgRef = useRef<HTMLImageElement | null>(null)
|
11 |
-
const
|
12 |
-
const
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
const getPixelColor = (x: number, y: number) => {
|
15 |
-
|
16 |
-
if (!context) {
|
17 |
throw new Error("Unable to get context from canvas")
|
18 |
}
|
19 |
|
20 |
-
const imgData =
|
21 |
|
22 |
-
|
|
|
|
|
|
|
23 |
}
|
24 |
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
image.onload = function () {
|
35 |
-
if (context) {
|
36 |
-
context.drawImage(image, 0, 0)
|
37 |
-
|
38 |
-
const clickedColor = getPixelColor(x, y) as any
|
39 |
-
|
40 |
-
let closestSegment: ImageSegment | null = null
|
41 |
-
|
42 |
-
let minDistance = Infinity
|
43 |
-
|
44 |
-
segments.forEach(segment => {
|
45 |
-
const segmentColor = segment.color.slice(0,3) // get the RGB part only
|
46 |
-
|
47 |
-
const distance = Math.sqrt(
|
48 |
-
Math.pow(clickedColor[0] - segmentColor[0], 2) +
|
49 |
-
Math.pow(clickedColor[1] - segmentColor[1], 2) +
|
50 |
-
Math.pow(clickedColor[2] - segmentColor[2], 2)
|
51 |
-
)
|
52 |
-
|
53 |
-
if(distance < minDistance) {
|
54 |
-
minDistance = distance;
|
55 |
-
closestSegment = segment;
|
56 |
-
}
|
57 |
-
})
|
58 |
|
59 |
-
|
60 |
-
console.log(closestSegment) // Here is the closest matching segment
|
61 |
-
}
|
62 |
-
}
|
63 |
|
64 |
-
|
65 |
-
|
66 |
-
} else {
|
67 |
-
console.log("No mask available, aborting..")
|
68 |
}
|
69 |
-
}
|
70 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
if (!assetUrl) {
|
73 |
return <div className="flex w-full h-screen items-center justify-center text-center">
|
@@ -75,14 +117,56 @@ export const ImageRenderer = ({
|
|
75 |
</div>
|
76 |
}
|
77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
return (
|
79 |
<div className="w-full py-8 px-2">
|
80 |
-
<
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
</div>
|
87 |
)
|
88 |
}
|
|
|
1 |
+
import { useEffect, useRef, useState } from "react"
|
2 |
|
3 |
import { ImageSegment, RenderedScene } from "@/app/types"
|
4 |
|
5 |
export const ImageRenderer = ({
|
6 |
+
rendered: {
|
7 |
+
assetUrl = "",
|
8 |
+
maskBase64 = "",
|
9 |
+
segments = []
|
10 |
+
},
|
11 |
+
onUserAction,
|
12 |
+
}: {
|
13 |
+
rendered: RenderedScene
|
14 |
+
onUserAction: (action: string) => void
|
15 |
+
}) => {
|
16 |
const imgRef = useRef<HTMLImageElement | null>(null)
|
17 |
+
const canvasRef = useRef<HTMLCanvasElement | null>(null)
|
18 |
+
const contextRef = useRef<CanvasRenderingContext2D | null>(null)
|
19 |
+
const [actionnable, setActionnable] = useState<string>("")
|
20 |
+
|
21 |
+
useEffect(() => {
|
22 |
+
if (maskBase64) {
|
23 |
+
console.log("maskBase64:", maskBase64)
|
24 |
+
const img = new Image();
|
25 |
+
img.onload = function () {
|
26 |
+
canvasRef.current = document.createElement('canvas');
|
27 |
+
canvasRef.current.width = img.width;
|
28 |
+
canvasRef.current.height = img.height;
|
29 |
+
contextRef.current = canvasRef.current.getContext('2d');
|
30 |
+
contextRef.current!.drawImage(img, 0, 0, img.width, img.height);
|
31 |
+
}
|
32 |
+
img.src = "data:image/png;base64," + maskBase64;
|
33 |
+
} else {
|
34 |
+
console.log("error, no maskBase64 detected!")
|
35 |
+
}
|
36 |
+
}, [maskBase64]);
|
37 |
+
|
38 |
+
|
39 |
const getPixelColor = (x: number, y: number) => {
|
40 |
+
if (!contextRef.current) {
|
|
|
41 |
throw new Error("Unable to get context from canvas")
|
42 |
}
|
43 |
|
44 |
+
const imgData = contextRef.current.getImageData(x, y, 1, 1).data
|
45 |
|
46 |
+
|
47 |
+
const clickedColor = Array.from(imgData.slice(0, 3), value => value / 255);
|
48 |
+
|
49 |
+
return clickedColor
|
50 |
}
|
51 |
|
52 |
+
|
53 |
+
const getSegmentAt = (x: number, y: number): ImageSegment => {
|
54 |
+
if (!contextRef.current) throw new Error("Unable to get context from canvas");
|
55 |
+
if (!maskBase64) throw new Error("Mask is undefined");
|
56 |
+
|
57 |
+
let closestSegment: ImageSegment = {
|
58 |
+
id: 0,
|
59 |
+
box: [],
|
60 |
+
color: [],
|
61 |
+
label: "",
|
62 |
+
score: 0,
|
63 |
+
}
|
64 |
+
|
65 |
+
const clickedColor = getPixelColor(x,y) as any
|
66 |
+
|
67 |
+
if (`${clickedColor}` === "1,1,1") {
|
68 |
+
return closestSegment
|
69 |
+
}
|
70 |
|
71 |
+
let minDistance = Infinity;
|
72 |
+
|
73 |
+
segments.forEach(segment => {
|
74 |
+
const segmentColor = segment.color.slice(0,3); // get the RGB part only
|
75 |
+
|
76 |
+
const distance = Math.sqrt(
|
77 |
+
Math.pow(clickedColor[0] - segmentColor[0], 2) +
|
78 |
+
Math.pow(clickedColor[1] - segmentColor[1], 2) +
|
79 |
+
Math.pow(clickedColor[2] - segmentColor[2], 2)
|
80 |
+
);
|
81 |
+
|
82 |
+
if(distance < minDistance) {
|
83 |
+
minDistance = distance;
|
84 |
+
closestSegment = segment;
|
85 |
+
}
|
86 |
+
});
|
87 |
+
|
88 |
+
return closestSegment;
|
89 |
+
}
|
90 |
+
|
91 |
+
const handleMouseEvent = (event: React.MouseEvent, isClickEvent: boolean = false) => {
|
92 |
+
if (!contextRef.current) return; // Return early if mask image has not been loaded yet
|
93 |
|
94 |
+
const boundingRect = imgRef.current!.getBoundingClientRect();
|
95 |
+
const x = event.clientX - boundingRect.left;
|
96 |
+
const y = event.clientY - boundingRect.top;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
+
const newSegment = getSegmentAt(x, y)
|
|
|
|
|
|
|
99 |
|
100 |
+
if (actionnable !== newSegment.label) {
|
101 |
+
setActionnable(newSegment.label)
|
|
|
|
|
102 |
}
|
|
|
103 |
|
104 |
+
if (!newSegment.label) { return }
|
105 |
+
|
106 |
+
console.log("actionnable: ", actionnable)
|
107 |
+
|
108 |
+
if (isClickEvent) {
|
109 |
+
console.log("User clicked on " + actionnable);
|
110 |
+
// onUserAction(actionnable);
|
111 |
+
}
|
112 |
+
};
|
113 |
|
114 |
if (!assetUrl) {
|
115 |
return <div className="flex w-full h-screen items-center justify-center text-center">
|
|
|
117 |
</div>
|
118 |
}
|
119 |
|
120 |
+
/*
|
121 |
+
<img
|
122 |
+
src={assetUrl}
|
123 |
+
ref={imgRef}
|
124 |
+
width="1024px"
|
125 |
+
height="512px"
|
126 |
+
className={
|
127 |
+
[
|
128 |
+
"absolute top-0 left-0",
|
129 |
+
actionnable ? "cursor-pointer" : ""
|
130 |
+
].join(" ")
|
131 |
+
}
|
132 |
+
onMouseDown={(event) => handleMouseEvent(event, true)}
|
133 |
+
onMouseMove={handleMouseEvent}
|
134 |
+
/>
|
135 |
+
|
136 |
+
<img
|
137 |
+
src={"data:image/png;base64," + maskBase64}
|
138 |
+
ref={imgRef}
|
139 |
+
width="1024px"
|
140 |
+
height="512px"
|
141 |
+
className={
|
142 |
+
[
|
143 |
+
"absolute top-0 left-0 opacity-30",
|
144 |
+
actionnable ? "cursor-pointer" : ""
|
145 |
+
].join(" ")
|
146 |
+
}
|
147 |
+
onMouseDown={(event) => handleMouseEvent(event, true)}
|
148 |
+
onMouseMove={handleMouseEvent}
|
149 |
+
/>
|
150 |
+
*/
|
151 |
+
|
152 |
return (
|
153 |
<div className="w-full py-8 px-2">
|
154 |
+
<div className="relative w-full">
|
155 |
+
<img
|
156 |
+
src={"data:image/png;base64," + maskBase64}
|
157 |
+
ref={imgRef}
|
158 |
+
width="1024px"
|
159 |
+
height="512px"
|
160 |
+
className={
|
161 |
+
[
|
162 |
+
"absolute top-0 left-0 opacity-30",
|
163 |
+
actionnable ? "cursor-pointer" : ""
|
164 |
+
].join(" ")
|
165 |
+
}
|
166 |
+
onMouseDown={(event) => handleMouseEvent(event, true)}
|
167 |
+
onMouseMove={handleMouseEvent}
|
168 |
+
/>
|
169 |
+
</div>
|
170 |
</div>
|
171 |
)
|
172 |
}
|