Spaces:
Runtime error
Runtime error
Commit
•
f0dc1c3
1
Parent(s):
3f09ee7
up
Browse files- package-lock.json +33 -0
- package.json +5 -0
- src/app/main.tsx +25 -65
- src/app/prompts.ts +0 -86
- src/app/queries/getActionnables.ts +86 -0
- src/app/queries/getBackground.ts +61 -0
- src/app/queries/getBase.ts +13 -0
- src/app/queries/getDialogue.ts +67 -0
- src/app/queries/getMusicTrack.ts +21 -0
- src/app/queries/getSoundTrack.ts +22 -0
- src/app/{predict.ts → queries/predict.ts} +2 -0
- src/components/business/image-renderer.tsx +22 -6
- src/lib/parseJsonList.ts +13 -0
package-lock.json
CHANGED
@@ -44,7 +44,9 @@
|
|
44 |
"next": "13.4.10",
|
45 |
"pick": "^0.0.1",
|
46 |
"postcss": "8.4.26",
|
|
|
47 |
"react": "18.2.0",
|
|
|
48 |
"react-day-picker": "^8.8.0",
|
49 |
"react-dom": "18.2.0",
|
50 |
"tailwind-merge": "^1.13.2",
|
@@ -56,6 +58,9 @@
|
|
56 |
"uuid": "^9.0.0",
|
57 |
"zod": "^3.21.4",
|
58 |
"zustand": "^4.3.9"
|
|
|
|
|
|
|
59 |
}
|
60 |
},
|
61 |
"node_modules/@aashutoshrathi/word-wrap": {
|
@@ -1494,6 +1499,12 @@
|
|
1494 |
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
1495 |
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
1496 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
1497 |
"node_modules/@types/react": {
|
1498 |
"version": "18.2.15",
|
1499 |
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz",
|
@@ -4824,6 +4835,20 @@
|
|
4824 |
"node": ">=6"
|
4825 |
}
|
4826 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4827 |
"node_modules/queue-microtask": {
|
4828 |
"version": "1.2.3",
|
4829 |
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
@@ -4854,6 +4879,14 @@
|
|
4854 |
"node": ">=0.10.0"
|
4855 |
}
|
4856 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4857 |
"node_modules/react-day-picker": {
|
4858 |
"version": "8.8.0",
|
4859 |
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.8.0.tgz",
|
|
|
44 |
"next": "13.4.10",
|
45 |
"pick": "^0.0.1",
|
46 |
"postcss": "8.4.26",
|
47 |
+
"qs": "^6.11.2",
|
48 |
"react": "18.2.0",
|
49 |
+
"react-circular-progressbar": "^2.1.0",
|
50 |
"react-day-picker": "^8.8.0",
|
51 |
"react-dom": "18.2.0",
|
52 |
"tailwind-merge": "^1.13.2",
|
|
|
58 |
"uuid": "^9.0.0",
|
59 |
"zod": "^3.21.4",
|
60 |
"zustand": "^4.3.9"
|
61 |
+
},
|
62 |
+
"devDependencies": {
|
63 |
+
"@types/qs": "^6.9.7"
|
64 |
}
|
65 |
},
|
66 |
"node_modules/@aashutoshrathi/word-wrap": {
|
|
|
1499 |
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
|
1500 |
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
|
1501 |
},
|
1502 |
+
"node_modules/@types/qs": {
|
1503 |
+
"version": "6.9.7",
|
1504 |
+
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
|
1505 |
+
"integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
|
1506 |
+
"dev": true
|
1507 |
+
},
|
1508 |
"node_modules/@types/react": {
|
1509 |
"version": "18.2.15",
|
1510 |
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz",
|
|
|
4835 |
"node": ">=6"
|
4836 |
}
|
4837 |
},
|
4838 |
+
"node_modules/qs": {
|
4839 |
+
"version": "6.11.2",
|
4840 |
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
|
4841 |
+
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
|
4842 |
+
"dependencies": {
|
4843 |
+
"side-channel": "^1.0.4"
|
4844 |
+
},
|
4845 |
+
"engines": {
|
4846 |
+
"node": ">=0.6"
|
4847 |
+
},
|
4848 |
+
"funding": {
|
4849 |
+
"url": "https://github.com/sponsors/ljharb"
|
4850 |
+
}
|
4851 |
+
},
|
4852 |
"node_modules/queue-microtask": {
|
4853 |
"version": "1.2.3",
|
4854 |
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
|
|
4879 |
"node": ">=0.10.0"
|
4880 |
}
|
4881 |
},
|
4882 |
+
"node_modules/react-circular-progressbar": {
|
4883 |
+
"version": "2.1.0",
|
4884 |
+
"resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.1.0.tgz",
|
4885 |
+
"integrity": "sha512-xp4THTrod4aLpGy68FX/k1Q3nzrfHUjUe5v6FsdwXBl3YVMwgeXYQKDrku7n/D6qsJA9CuunarAboC2xCiKs1g==",
|
4886 |
+
"peerDependencies": {
|
4887 |
+
"react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
|
4888 |
+
}
|
4889 |
+
},
|
4890 |
"node_modules/react-day-picker": {
|
4891 |
"version": "8.8.0",
|
4892 |
"resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.8.0.tgz",
|
package.json
CHANGED
@@ -45,7 +45,9 @@
|
|
45 |
"next": "13.4.10",
|
46 |
"pick": "^0.0.1",
|
47 |
"postcss": "8.4.26",
|
|
|
48 |
"react": "18.2.0",
|
|
|
49 |
"react-day-picker": "^8.8.0",
|
50 |
"react-dom": "18.2.0",
|
51 |
"tailwind-merge": "^1.13.2",
|
@@ -57,5 +59,8 @@
|
|
57 |
"uuid": "^9.0.0",
|
58 |
"zod": "^3.21.4",
|
59 |
"zustand": "^4.3.9"
|
|
|
|
|
|
|
60 |
}
|
61 |
}
|
|
|
45 |
"next": "13.4.10",
|
46 |
"pick": "^0.0.1",
|
47 |
"postcss": "8.4.26",
|
48 |
+
"qs": "^6.11.2",
|
49 |
"react": "18.2.0",
|
50 |
+
"react-circular-progressbar": "^2.1.0",
|
51 |
"react-day-picker": "^8.8.0",
|
52 |
"react-dom": "18.2.0",
|
53 |
"tailwind-merge": "^1.13.2",
|
|
|
59 |
"uuid": "^9.0.0",
|
60 |
"zod": "^3.21.4",
|
61 |
"zustand": "^4.3.9"
|
62 |
+
},
|
63 |
+
"devDependencies": {
|
64 |
+
"@types/qs": "^6.9.7"
|
65 |
}
|
66 |
}
|
src/app/main.tsx
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
"use client"
|
2 |
|
3 |
import { useEffect, useRef, useState, useTransition } from "react"
|
|
|
4 |
|
5 |
import { ImageRenderer } from "@/components/business/image-renderer"
|
6 |
|
@@ -15,10 +16,11 @@ import {
|
|
15 |
import { render } from "./render"
|
16 |
|
17 |
import { RenderedScene } from "./types"
|
18 |
-
import { predict } from "./predict"
|
19 |
import { GameType } from "./games/types"
|
20 |
import { defaultGame, games, getGame } from "./games"
|
21 |
-
import {
|
|
|
|
|
22 |
|
23 |
export default function Main() {
|
24 |
const [isPending, startTransition] = useTransition()
|
@@ -28,7 +30,9 @@ export default function Main() {
|
|
28 |
maskBase64: "",
|
29 |
segments:[]
|
30 |
})
|
31 |
-
const
|
|
|
|
|
32 |
const [situation, setSituation] = useState("")
|
33 |
const [scene, setScene] = useState("")
|
34 |
const [dialogue, setDialogue] = useState("")
|
@@ -92,75 +96,31 @@ export default function Main() {
|
|
92 |
|
93 |
const game = getGame(ref.current)
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
console.log("prompts:", prompts)
|
98 |
-
|
99 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
console.log(`rawSituation: `, rawSituation)
|
106 |
-
|
107 |
-
if (!rawSituation) {
|
108 |
-
throw new Error("failed to generate the situation")
|
109 |
-
}
|
110 |
-
const newSituation = `${rawSituation || ""}`
|
111 |
-
if (!newSituation) {
|
112 |
-
throw new Error("failed to parse the situation")
|
113 |
-
}
|
114 |
-
|
115 |
-
console.log(`newSituation: `, newSituation)
|
116 |
-
|
117 |
-
const rawActionnables = await predict(prompts.actionnablesPrompt)
|
118 |
-
console.log(`rawActionnables: `, rawActionnables)
|
119 |
-
|
120 |
-
if (!rawActionnables) {
|
121 |
-
throw new Error("failed to generate the actionnables")
|
122 |
-
}
|
123 |
-
|
124 |
-
let newActionnables = []
|
125 |
-
try {
|
126 |
-
// we remove all [ or ]
|
127 |
-
const sanitized = rawActionnables.replaceAll("[", "").replaceAll("]", "")
|
128 |
-
newActionnables = (JSON.parse(`[${sanitized}]`) as string[]).map(item =>
|
129 |
-
// clean the words to remove any punctuation
|
130 |
-
item.replace(/\W/g, '').trim()
|
131 |
-
)
|
132 |
-
|
133 |
-
if (!newActionnables.length) {
|
134 |
-
throw new Error("no actionnables")
|
135 |
-
}
|
136 |
-
} catch (err) {
|
137 |
-
throw new Error("failed to parse the actionnables")
|
138 |
-
}
|
139 |
-
|
140 |
-
console.log(`newActionnables: `, newActionnables)
|
141 |
-
|
142 |
-
const rawDialogue = await predict(prompts.dialoguePrompt)
|
143 |
-
console.log(`rawDialogue: `, rawDialogue)
|
144 |
-
|
145 |
-
if (!rawDialogue) {
|
146 |
-
throw new Error("failed to generate the dialogue")
|
147 |
-
}
|
148 |
-
const newDialogue = `${rawDialogue || ""}`
|
149 |
-
if (!newDialogue) {
|
150 |
-
throw new Error("failed to parse the dialogue")
|
151 |
-
}
|
152 |
-
console.log(`newDialogue: `, newDialogue)
|
153 |
-
|
154 |
|
155 |
-
|
156 |
-
|
|
|
157 |
|
158 |
console.log("loading next scene..")
|
159 |
-
await loadNextScene(
|
160 |
|
161 |
-
|
162 |
} catch (err) {
|
163 |
-
|
|
|
164 |
}
|
165 |
})
|
166 |
}
|
|
|
1 |
"use client"
|
2 |
|
3 |
import { useEffect, useRef, useState, useTransition } from "react"
|
4 |
+
import qs from "qs"
|
5 |
|
6 |
import { ImageRenderer } from "@/components/business/image-renderer"
|
7 |
|
|
|
16 |
import { render } from "./render"
|
17 |
|
18 |
import { RenderedScene } from "./types"
|
|
|
19 |
import { GameType } from "./games/types"
|
20 |
import { defaultGame, games, getGame } from "./games"
|
21 |
+
import { getBackground } from "@/app/queries/getBackground"
|
22 |
+
import { getDialogue } from "@/app/queries/getDialogue"
|
23 |
+
import { getActionnables } from "@/app/queries/getActionnables"
|
24 |
|
25 |
export default function Main() {
|
26 |
const [isPending, startTransition] = useTransition()
|
|
|
30 |
maskBase64: "",
|
31 |
segments:[]
|
32 |
})
|
33 |
+
const urlParams = qs.parse(window.location.search.slice(1))
|
34 |
+
console.log("urlParams:", urlParams)
|
35 |
+
const ref = useRef<GameType>(`${urlParams?.game as any}` as GameType)
|
36 |
const [situation, setSituation] = useState("")
|
37 |
const [scene, setScene] = useState("")
|
38 |
const [dialogue, setDialogue] = useState("")
|
|
|
96 |
|
97 |
const game = getGame(ref.current)
|
98 |
|
99 |
+
let newDialogue = ""
|
|
|
|
|
|
|
100 |
try {
|
101 |
+
newDialogue = await getDialogue({ game, situation, actionnable })
|
102 |
+
console.log(`newDialogue:`, newDialogue)
|
103 |
+
setDialogue(newDialogue)
|
104 |
+
} catch (err) {
|
105 |
+
console.log(`failed to generate dialogue (but it's only a nice to have, so..)`)
|
106 |
+
setDialogue("")
|
107 |
+
}
|
108 |
|
109 |
+
try {
|
110 |
+
const newActionnables = await getActionnables({ game, situation, actionnable, newDialogue })
|
111 |
+
console.log(`newActionnables:`, newActionnables)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
113 |
+
const newBackground = await getBackground({ game, situation, actionnable, newDialogue, newActionnables })
|
114 |
+
console.log(`newBackground:`, newBackground)
|
115 |
+
setSituation(newBackground)
|
116 |
|
117 |
console.log("loading next scene..")
|
118 |
+
await loadNextScene(newBackground, newActionnables)
|
119 |
|
120 |
+
// todo we could also use useEffect
|
121 |
} catch (err) {
|
122 |
+
console.error(`failed to get one of the mandatory entites: ${err}`)
|
123 |
+
setLoading(false)
|
124 |
}
|
125 |
})
|
126 |
}
|
src/app/prompts.ts
DELETED
@@ -1,86 +0,0 @@
|
|
1 |
-
import { Game } from "./games/types"
|
2 |
-
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
3 |
-
|
4 |
-
export const getPrompts = (game: Game, situation: string = "", actionnable: string = "") => {
|
5 |
-
|
6 |
-
const initialPrompt = [...game.getScenePrompt()].join(", ")
|
7 |
-
|
8 |
-
const currentPrompt = situation
|
9 |
-
? [...game.getScenePrompt(situation)].join(", ")
|
10 |
-
: initialPrompt
|
11 |
-
|
12 |
-
const userSituationPrompt = `Player is currently in "${currentPrompt}". Player clicked on the "${actionnable}".`
|
13 |
-
|
14 |
-
const baseSituationPromptIfWeHaveHistory = initialPrompt !== currentPrompt
|
15 |
-
? ` You must imagine the most plausible next scene, based on where the player was located before and is now, and also what the player did before and are doing now.
|
16 |
-
Here is the original scene in which the user was located at first, which will inform you about the general settings to follow (you must respect this): "${initialPrompt}".`
|
17 |
-
: ""
|
18 |
-
|
19 |
-
const situationPrompt = createLlamaPrompt([
|
20 |
-
{
|
21 |
-
role: "system",
|
22 |
-
content: [
|
23 |
-
`You are the AI game master of a role video game.`,
|
24 |
-
baseSituationPromptIfWeHaveHistory,
|
25 |
-
`You are going to receive new information about the current whereabouts of the player.`,
|
26 |
-
`Please write a caption for the next plausible 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).`
|
27 |
-
].filter(item => item).join("\n")
|
28 |
-
},
|
29 |
-
{
|
30 |
-
role: "user",
|
31 |
-
content: userSituationPrompt
|
32 |
-
}
|
33 |
-
])
|
34 |
-
|
35 |
-
const actionnablesPrompt = createLlamaPrompt([
|
36 |
-
{
|
37 |
-
role: "system",
|
38 |
-
content: [
|
39 |
-
`You are an API endpoint that can return a list of objects thare are in a scene.`,
|
40 |
-
`You must list basic name of things (eg. "parrot", "chest", "spaceship", "glass", "door", "person", "window", "light", "knob", "button" etc)`,
|
41 |
-
`The answer must be a JSON array, ie. a list of quoted strings.`
|
42 |
-
].filter(item => item).join("\n")
|
43 |
-
},
|
44 |
-
{
|
45 |
-
role: "user",
|
46 |
-
content: userSituationPrompt
|
47 |
-
}
|
48 |
-
])
|
49 |
-
|
50 |
-
const baseDialoguePromptIfWeHaveHistory = initialPrompt !== currentPrompt
|
51 |
-
? `for your information, the initial game panel and scene was: "${initialPrompt}".`
|
52 |
-
: ""
|
53 |
-
|
54 |
-
const dialoguePrompt = createLlamaPrompt([
|
55 |
-
{
|
56 |
-
role: "system",
|
57 |
-
content: [
|
58 |
-
`You are the AI game master of a role video game.`,
|
59 |
-
`You are going to receive new information about the current whereabouts and action of the player.`,
|
60 |
-
baseDialoguePromptIfWeHaveHistory,
|
61 |
-
`You must imagine a funny response to speak in reaction to what the player did, like in some old point and click video games.`,
|
62 |
-
`Please limit yourself to only a 1 or 2 sentences, please.`,
|
63 |
-
].filter(item => item).join("\n")
|
64 |
-
},
|
65 |
-
{
|
66 |
-
role: "user",
|
67 |
-
content: userSituationPrompt
|
68 |
-
}
|
69 |
-
])
|
70 |
-
|
71 |
-
|
72 |
-
const prompts = {
|
73 |
-
initialPrompt,
|
74 |
-
currentPrompt,
|
75 |
-
userSituationPrompt,
|
76 |
-
baseSituationPromptIfWeHaveHistory,
|
77 |
-
situationPrompt,
|
78 |
-
actionnablesPrompt,
|
79 |
-
baseDialoguePromptIfWeHaveHistory,
|
80 |
-
dialoguePrompt,
|
81 |
-
}
|
82 |
-
|
83 |
-
return prompts
|
84 |
-
}
|
85 |
-
|
86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/app/queries/getActionnables.ts
ADDED
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Game } from "@/app/games/types"
|
2 |
+
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
3 |
+
import { parseJsonList } from "@/lib/parseJsonList"
|
4 |
+
|
5 |
+
import { getBase } from "./getBase"
|
6 |
+
import { predict } from "./predict"
|
7 |
+
|
8 |
+
export const getActionnables = async ({
|
9 |
+
game,
|
10 |
+
situation = "",
|
11 |
+
actionnable = "",
|
12 |
+
newDialogue = "",
|
13 |
+
// newActionnables = [],
|
14 |
+
}: {
|
15 |
+
game: Game;
|
16 |
+
situation: string;
|
17 |
+
actionnable: string;
|
18 |
+
newDialogue: string;
|
19 |
+
// newActionnables: string[];
|
20 |
+
}) => {
|
21 |
+
|
22 |
+
const { currentPrompt, initialPrompt, userSituationPrompt } = getBase(game, situation, actionnable)
|
23 |
+
|
24 |
+
const basePrompt = initialPrompt !== currentPrompt
|
25 |
+
? `Here is some context information about the initial scene: ${initialPrompt}`
|
26 |
+
: ""
|
27 |
+
|
28 |
+
const prompt = createLlamaPrompt([
|
29 |
+
{
|
30 |
+
role: "system",
|
31 |
+
content: [
|
32 |
+
`You are an API endpoint that can return a list of objects visible in the background image of a role playing game.`,
|
33 |
+
basePrompt,
|
34 |
+
`You must list basic name of characters or visible objects (eg. "parrot", "chest", "spaceship", "glass", "door", "person", "window", "light", "knob", "button" etc) but don't list any word from abstract concepts (ig. don't say things like "secret", "danger", "next move" etc)`,
|
35 |
+
`The answer must be a JSON array, ie. a list of quoted strings.`
|
36 |
+
].filter(item => item).join("\n")
|
37 |
+
},
|
38 |
+
{
|
39 |
+
role: "user",
|
40 |
+
content: userSituationPrompt
|
41 |
+
}
|
42 |
+
])
|
43 |
+
|
44 |
+
let rawStringOutput = ""
|
45 |
+
|
46 |
+
try {
|
47 |
+
rawStringOutput = await predict(prompt)
|
48 |
+
} catch (err) {
|
49 |
+
console.log(`prediction of the actionnables failed, trying again..`)
|
50 |
+
try {
|
51 |
+
rawStringOutput = await predict(prompt)
|
52 |
+
} catch (err) {
|
53 |
+
console.error(`prediction of the actionnables failed again!`)
|
54 |
+
throw new Error(`failed to generate the actionnables ${err}`)
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
let result = []
|
59 |
+
|
60 |
+
try {
|
61 |
+
result = parseJsonList(rawStringOutput)
|
62 |
+
|
63 |
+
if (!result.length) {
|
64 |
+
throw new Error("no actionnables")
|
65 |
+
}
|
66 |
+
} catch (err) {
|
67 |
+
console.log("failed to find a valid JSON! attempting method 2..")
|
68 |
+
|
69 |
+
try {
|
70 |
+
const sanitized = rawStringOutput.replaceAll("[", "").replaceAll("]", "")
|
71 |
+
result = (JSON.parse(`[${sanitized}]`) as string[]).map(item =>
|
72 |
+
// clean the words to remove any punctuation
|
73 |
+
item.replace(/\W/g, '').trim()
|
74 |
+
)
|
75 |
+
|
76 |
+
if (!result.length) {
|
77 |
+
throw new Error("no actionnables")
|
78 |
+
}
|
79 |
+
} catch (err) {
|
80 |
+
console.log("failed to repair and recover a valid JSON!")
|
81 |
+
throw new Error("failed to parse the actionnables")
|
82 |
+
}
|
83 |
+
}
|
84 |
+
|
85 |
+
return result
|
86 |
+
}
|
src/app/queries/getBackground.ts
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Game } from "@/app/games/types"
|
2 |
+
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
3 |
+
|
4 |
+
import { getBase } from "./getBase"
|
5 |
+
import { predict } from "./predict"
|
6 |
+
|
7 |
+
export const getBackground = async ({
|
8 |
+
game,
|
9 |
+
situation = "",
|
10 |
+
actionnable = "",
|
11 |
+
newDialogue = "",
|
12 |
+
newActionnables = [],
|
13 |
+
}: {
|
14 |
+
game: Game;
|
15 |
+
situation: string;
|
16 |
+
actionnable: string;
|
17 |
+
newDialogue: string;
|
18 |
+
newActionnables: string[];
|
19 |
+
}) => {
|
20 |
+
|
21 |
+
const { currentPrompt, initialPrompt, userSituationPrompt } = getBase(game, situation, actionnable)
|
22 |
+
|
23 |
+
const basePrompt = initialPrompt !== currentPrompt
|
24 |
+
? `You must imagine the most plausible next scene, based on where the player was located before and is now, and also what the player did before and are doing now.
|
25 |
+
Here is the original scene in which the user was located at first, which will inform you about the general settings to follow (you must respect this): "${initialPrompt}".`
|
26 |
+
: ""
|
27 |
+
|
28 |
+
const prompt = createLlamaPrompt([
|
29 |
+
{
|
30 |
+
role: "system",
|
31 |
+
content: [
|
32 |
+
`You are the AI game master of a role video game.`,
|
33 |
+
basePrompt,
|
34 |
+
`You are going to receive new information about the current whereabouts of the player.`,
|
35 |
+
`Please write a caption for the next plausible scene to display in intricate details: the environment, lights, era, characters, objects, textures, light etc.`,
|
36 |
+
`You must include important objects, that the user can click on (eg. characters, doors, vehicles, useful objects).`,
|
37 |
+
`Be straight to the point, and do not say things like "As the player clicks on.." or "the scene shifts to" (the best is not not mention the player at all)`
|
38 |
+
].filter(item => item).join("\n")
|
39 |
+
},
|
40 |
+
{
|
41 |
+
role: "user",
|
42 |
+
content: userSituationPrompt
|
43 |
+
}
|
44 |
+
])
|
45 |
+
|
46 |
+
|
47 |
+
let result = ""
|
48 |
+
try {
|
49 |
+
result = await predict(prompt)
|
50 |
+
} catch (err) {
|
51 |
+
console.log(`prediction of the background failed, trying again..`)
|
52 |
+
try {
|
53 |
+
result = await predict(prompt)
|
54 |
+
} catch (err) {
|
55 |
+
console.error(`prediction of the background failed again!`)
|
56 |
+
throw new Error(`failed to generate the background ${err}`)
|
57 |
+
}
|
58 |
+
}
|
59 |
+
|
60 |
+
return result
|
61 |
+
}
|
src/app/queries/getBase.ts
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Game } from "@/app/games/types"
|
2 |
+
|
3 |
+
export const getBase = (game: Game, situation: string = "", actionnable: string = "") => {
|
4 |
+
const initialPrompt = [...game.getScenePrompt()].join(", ")
|
5 |
+
|
6 |
+
const currentPrompt = situation
|
7 |
+
? [...game.getScenePrompt(situation)].join(", ")
|
8 |
+
: initialPrompt
|
9 |
+
|
10 |
+
const userSituationPrompt = `Player is currently in "${currentPrompt}". Player clicked on the "${actionnable}".`
|
11 |
+
|
12 |
+
return { initialPrompt, currentPrompt, userSituationPrompt }
|
13 |
+
}
|
src/app/queries/getDialogue.ts
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Game } from "@/app/games/types"
|
2 |
+
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
3 |
+
|
4 |
+
import { getBase } from "./getBase"
|
5 |
+
import { predict } from "./predict"
|
6 |
+
|
7 |
+
|
8 |
+
export const getDialogue = async ({
|
9 |
+
game,
|
10 |
+
situation = "",
|
11 |
+
actionnable = "",
|
12 |
+
// newDialogue = "",
|
13 |
+
// newActionnables = [],
|
14 |
+
}: {
|
15 |
+
game: Game;
|
16 |
+
situation: string;
|
17 |
+
actionnable: string;
|
18 |
+
// newDialogue: string;
|
19 |
+
// newActionnables: string[];
|
20 |
+
}) => {
|
21 |
+
|
22 |
+
const { currentPrompt, initialPrompt, userSituationPrompt } = getBase(game, situation, actionnable)
|
23 |
+
|
24 |
+
/*
|
25 |
+
const basePrompt = initialPrompt !== currentPrompt
|
26 |
+
? `for your information, the initial game panel and scene was: ${initialPrompt}`
|
27 |
+
: ""
|
28 |
+
*/
|
29 |
+
|
30 |
+
const basePrompt = initialPrompt !== currentPrompt
|
31 |
+
? ` You must imagine the most plausible next dialogue line from the game master, based on where the player was located before and is now, and also what the player did before and are doing now.
|
32 |
+
Here is the original scene in which the user was located at first, which will inform you about the general settings to follow (you must respect this): "${initialPrompt}".`
|
33 |
+
: ""
|
34 |
+
|
35 |
+
const prompt = createLlamaPrompt([
|
36 |
+
{
|
37 |
+
role: "system",
|
38 |
+
content: [
|
39 |
+
`You are the AI game master of a role video game.`,
|
40 |
+
`You are going to receive new information about the current whereabouts and action of the player.`,
|
41 |
+
basePrompt,
|
42 |
+
`You must imagine a funny response to speak in reaction to what the player did, like in some old point and click video games.`,
|
43 |
+
`Please limit yourself to only a 1 or 2 sentences, please.`,
|
44 |
+
].filter(item => item).join("\n")
|
45 |
+
},
|
46 |
+
{
|
47 |
+
role: "user",
|
48 |
+
content: userSituationPrompt
|
49 |
+
}
|
50 |
+
])
|
51 |
+
|
52 |
+
|
53 |
+
let result = ""
|
54 |
+
try {
|
55 |
+
result = await predict(prompt)
|
56 |
+
} catch (err) {
|
57 |
+
console.log(`prediction of the dialogue failed, trying again..`)
|
58 |
+
try {
|
59 |
+
result = await predict(prompt)
|
60 |
+
} catch (err) {
|
61 |
+
console.error(`prediction of the dialogue failed again!`)
|
62 |
+
throw new Error(`failed to generate the dialogue ${err}`)
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
return result
|
67 |
+
}
|
src/app/queries/getMusicTrack.ts
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Game } from "@/app/games/types"
|
2 |
+
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
3 |
+
|
4 |
+
import { getBase } from "./getBase"
|
5 |
+
import { predict } from "./predict"
|
6 |
+
|
7 |
+
export const getMusicTrack = async ({
|
8 |
+
game,
|
9 |
+
situation = "",
|
10 |
+
actionnable = "",
|
11 |
+
newDialogue = "",
|
12 |
+
newActionnables = [],
|
13 |
+
}: {
|
14 |
+
game: Game;
|
15 |
+
situation: string;
|
16 |
+
actionnable: string;
|
17 |
+
newDialogue: string;
|
18 |
+
newActionnables: string[];
|
19 |
+
}) => {
|
20 |
+
return ""
|
21 |
+
}
|
src/app/queries/getSoundTrack.ts
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { Game } from "@/app/games/types"
|
2 |
+
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
3 |
+
|
4 |
+
import { getBase } from "./getBase"
|
5 |
+
import { predict } from "./predict"
|
6 |
+
|
7 |
+
export const getSoundTrack = async ({
|
8 |
+
game,
|
9 |
+
situation = "",
|
10 |
+
actionnable = "",
|
11 |
+
newDialogue = "",
|
12 |
+
newActionnables = [],
|
13 |
+
}: {
|
14 |
+
game: Game;
|
15 |
+
situation: string;
|
16 |
+
actionnable: string;
|
17 |
+
newDialogue: string;
|
18 |
+
newActionnables: string[];
|
19 |
+
}) => {
|
20 |
+
|
21 |
+
return ""
|
22 |
+
}
|
src/app/{predict.ts → queries/predict.ts}
RENAMED
@@ -7,6 +7,8 @@ const hf = hfi.endpoint(`${process.env.HF_INFERENCE_ENDPOINT_URL || ""}`)
|
|
7 |
|
8 |
export async function predict(inputs: string) {
|
9 |
|
|
|
|
|
10 |
let instructions = ""
|
11 |
try {
|
12 |
for await (const output of hf.textGenerationStream({
|
|
|
7 |
|
8 |
export async function predict(inputs: string) {
|
9 |
|
10 |
+
console.log(`predict: `, inputs)
|
11 |
+
|
12 |
let instructions = ""
|
13 |
try {
|
14 |
for await (const output of hf.textGenerationStream({
|
src/components/business/image-renderer.tsx
CHANGED
@@ -33,7 +33,9 @@ export const ImageRenderer = ({
|
|
33 |
canvasRef.current = document.createElement('canvas');
|
34 |
canvasRef.current.width = img.width;
|
35 |
canvasRef.current.height = img.height;
|
36 |
-
contextRef.current = canvasRef.current.getContext('2d'
|
|
|
|
|
37 |
contextRef.current!.drawImage(img, 0, 0, img.width, img.height);
|
38 |
}
|
39 |
img.src = "data:image/png;base64," + maskBase64;
|
@@ -104,6 +106,14 @@ export const ImageRenderer = ({
|
|
104 |
return false
|
105 |
}
|
106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
const boundingRect = imgRef.current!.getBoundingClientRect();
|
108 |
const x = event.clientX - boundingRect.left;
|
109 |
const y = event.clientY - boundingRect.top;
|
@@ -112,10 +122,12 @@ export const ImageRenderer = ({
|
|
112 |
|
113 |
if (actionnable !== newSegment.label) {
|
114 |
if (newSegment.label) {
|
115 |
-
console.log(`User is hovering "${newSegment.label}"`)
|
116 |
} else {
|
117 |
-
console.log(`Nothing in the area`)
|
118 |
}
|
|
|
|
|
119 |
setActionnable(newSegment.label)
|
120 |
}
|
121 |
|
@@ -126,7 +138,11 @@ export const ImageRenderer = ({
|
|
126 |
console.log("User clicked on " + newSegment.label)
|
127 |
onUserAction(actionnable)
|
128 |
} else {
|
129 |
-
|
|
|
|
|
|
|
|
|
130 |
}
|
131 |
};
|
132 |
|
@@ -142,9 +158,9 @@ export const ImageRenderer = ({
|
|
142 |
return
|
143 |
}
|
144 |
|
145 |
-
console.log("still loading..")
|
146 |
|
147 |
-
console.log("updating progress")
|
148 |
progress = progress + 1
|
149 |
setProcessPercent(progress)
|
150 |
|
|
|
33 |
canvasRef.current = document.createElement('canvas');
|
34 |
canvasRef.current.width = img.width;
|
35 |
canvasRef.current.height = img.height;
|
36 |
+
contextRef.current = canvasRef.current.getContext('2d', {
|
37 |
+
willReadFrequently: true
|
38 |
+
});
|
39 |
contextRef.current!.drawImage(img, 0, 0, img.width, img.height);
|
40 |
}
|
41 |
img.src = "data:image/png;base64," + maskBase64;
|
|
|
106 |
return false
|
107 |
}
|
108 |
|
109 |
+
// sometimes we generate an image, but the segmentation fails
|
110 |
+
// so if we click anywhere bug there are no segments,
|
111 |
+
// we inform the rest of the app by passing nothing
|
112 |
+
if (isClickEvent && segments.length == 0) {
|
113 |
+
onUserAction("nothing, to trigger a scene reload")
|
114 |
+
return
|
115 |
+
}
|
116 |
+
|
117 |
const boundingRect = imgRef.current!.getBoundingClientRect();
|
118 |
const x = event.clientX - boundingRect.left;
|
119 |
const y = event.clientY - boundingRect.top;
|
|
|
122 |
|
123 |
if (actionnable !== newSegment.label) {
|
124 |
if (newSegment.label) {
|
125 |
+
console.log(`User is hovering "${newSegment.label}"`)
|
126 |
} else {
|
127 |
+
console.log(`Nothing in the area`)
|
128 |
}
|
129 |
+
|
130 |
+
// update the actionnable immediately, so we can show the hand / finger cursor pointer
|
131 |
setActionnable(newSegment.label)
|
132 |
}
|
133 |
|
|
|
138 |
console.log("User clicked on " + newSegment.label)
|
139 |
onUserAction(actionnable)
|
140 |
} else {
|
141 |
+
// only trigger hover events if there are segments,
|
142 |
+
// otherwise it's best to stay silent
|
143 |
+
if (segments.length) {
|
144 |
+
onUserHover(actionnable)
|
145 |
+
}
|
146 |
}
|
147 |
};
|
148 |
|
|
|
158 |
return
|
159 |
}
|
160 |
|
161 |
+
// console.log("still loading..")
|
162 |
|
163 |
+
// console.log("updating progress")
|
164 |
progress = progress + 1
|
165 |
setProcessPercent(progress)
|
166 |
|
src/lib/parseJsonList.ts
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function parseJsonList(content: string): string[] {
|
2 |
+
// Extract JSON array from the content
|
3 |
+
const start = content.indexOf("[");
|
4 |
+
const end = content.lastIndexOf("]");
|
5 |
+
const jsonContent = content.slice(start, end + 1);
|
6 |
+
|
7 |
+
// Parse as JSON into array of strings
|
8 |
+
let objects: string[] = [];
|
9 |
+
|
10 |
+
objects = JSON.parse(jsonContent);
|
11 |
+
|
12 |
+
return objects;
|
13 |
+
}
|