Commit
•
2f7798c
1
Parent(s):
a5a608f
try to improve robustness of LLM responses
Browse files- src/app/queries/getStory.ts +37 -23
- src/lib/dirtyCaptionCleaner.ts +3 -0
- src/lib/dirtyLLMJsonParser.ts +15 -0
- src/lib/dirtyLLMResponseCleaner.ts +8 -0
- src/types.ts +2 -0
src/app/queries/getStory.ts
CHANGED
@@ -1,10 +1,11 @@
|
|
1 |
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
|
|
|
|
|
|
2 |
|
3 |
import { predict } from "./predict"
|
4 |
import { Preset } from "../engine/presets"
|
5 |
|
6 |
-
type LLMResponse = Array<{panel: number; caption: string }>
|
7 |
-
|
8 |
export const getStory = async ({
|
9 |
preset,
|
10 |
prompt = "",
|
@@ -32,6 +33,7 @@ export const getStory = async ({
|
|
32 |
|
33 |
|
34 |
let result = ""
|
|
|
35 |
try {
|
36 |
result = await predict(query)
|
37 |
if (!result.trim().length) {
|
@@ -51,33 +53,45 @@ export const getStory = async ({
|
|
51 |
}
|
52 |
|
53 |
console.log("Raw response from LLM:", result)
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
.replaceAll("]]", "]")
|
58 |
-
.replaceAll(",,", ",")
|
59 |
|
60 |
try {
|
61 |
-
|
62 |
-
let jsonOrNot = `[${tmp.split("[").pop() || ""}`
|
63 |
-
|
64 |
-
// and before the first ]
|
65 |
-
jsonOrNot = `${jsonOrNot.split("]").shift() || ""}]`
|
66 |
-
|
67 |
-
const jsonData = JSON.parse(jsonOrNot) as LLMResponse
|
68 |
-
const captions = jsonData.map(item => item.caption.trim())
|
69 |
-
return captions.map(caption => caption.split(":").pop()?.trim() || "")
|
70 |
} catch (err) {
|
71 |
console.log(`failed to read LLM response: ${err}`)
|
72 |
|
73 |
-
//
|
74 |
-
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
|
|
|
|
80 |
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
}
|
|
|
|
|
83 |
}
|
|
|
1 |
import { createLlamaPrompt } from "@/lib/createLlamaPrompt"
|
2 |
+
import { dirtyLLMResponseCleaner } from "@/lib/dirtyLLMResponseCleaner"
|
3 |
+
import { dirtyLLMJsonParser } from "@/lib/dirtyLLMJsonParser"
|
4 |
+
import { dirtyCaptionCleaner } from "@/lib/dirtyCaptionCleaner"
|
5 |
|
6 |
import { predict } from "./predict"
|
7 |
import { Preset } from "../engine/presets"
|
8 |
|
|
|
|
|
9 |
export const getStory = async ({
|
10 |
preset,
|
11 |
prompt = "",
|
|
|
33 |
|
34 |
|
35 |
let result = ""
|
36 |
+
|
37 |
try {
|
38 |
result = await predict(query)
|
39 |
if (!result.trim().length) {
|
|
|
53 |
}
|
54 |
|
55 |
console.log("Raw response from LLM:", result)
|
56 |
+
const tmp = dirtyLLMResponseCleaner(result)
|
57 |
+
|
58 |
+
let captions: string[] = []
|
|
|
|
|
59 |
|
60 |
try {
|
61 |
+
captions = dirtyLLMJsonParser(tmp)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
} catch (err) {
|
63 |
console.log(`failed to read LLM response: ${err}`)
|
64 |
|
65 |
+
// it is possible that the LLM has generated multiple JSON files like this:
|
66 |
+
|
67 |
+
/*
|
68 |
+
[ {
|
69 |
+
"panel": 1,
|
70 |
+
"caption": "A samurai stands at the edge of a bustling street in San Francisco, looking out of place among the hippies and beatniks."
|
71 |
+
} ]
|
72 |
|
73 |
+
[ {
|
74 |
+
"panel": 2,
|
75 |
+
"caption": "The samurai spots a group of young people playing music on the sidewalk. He approaches them, intrigued."
|
76 |
+
} ]
|
77 |
+
*/
|
78 |
+
try {
|
79 |
+
// in that case, we can try to repair it like so:
|
80 |
+
let strategy2 = `[${tmp.split("[").pop() || ""}`
|
81 |
+
strategy2.replaceAll("[", ",")
|
82 |
|
83 |
+
captions = dirtyLLMJsonParser(strategy2)
|
84 |
+
} catch (err2) {
|
85 |
+
|
86 |
+
// in case of failure here, it might be because the LLM hallucinated a completely different response,
|
87 |
+
// such as markdown. There is no real solution.. but we can try a fallback:
|
88 |
+
|
89 |
+
captions = (
|
90 |
+
tmp.split("*")
|
91 |
+
.map(item => item.replaceAll("[", "[").replaceAll("]", "]").trim())
|
92 |
+
)
|
93 |
+
}
|
94 |
}
|
95 |
+
|
96 |
+
return captions.map(caption => dirtyCaptionCleaner(caption))
|
97 |
}
|
src/lib/dirtyCaptionCleaner.ts
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
export function dirtyCaptionCleaner(input: string) {
|
2 |
+
return input.split(":").pop()?.trim() || ""
|
3 |
+
}
|
src/lib/dirtyLLMJsonParser.ts
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { LLMResponse } from "@/types"
|
2 |
+
|
3 |
+
export function dirtyLLMJsonParser(input: string): string[] {
|
4 |
+
// we only keep what's after the first [
|
5 |
+
let jsonOrNot = `[${input.split("[").pop() || ""}`
|
6 |
+
|
7 |
+
// and before the first ]
|
8 |
+
jsonOrNot = `${jsonOrNot.split("]").shift() || ""}]`
|
9 |
+
|
10 |
+
const jsonData = JSON.parse(jsonOrNot) as LLMResponse
|
11 |
+
|
12 |
+
const captions = jsonData.map(item => item.caption.trim())
|
13 |
+
|
14 |
+
return captions
|
15 |
+
}
|
src/lib/dirtyLLMResponseCleaner.ts
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export function dirtyLLMResponseCleaner(input: string) {
|
2 |
+
return (
|
3 |
+
`${input || ""}`
|
4 |
+
.replaceAll("}}", "}")
|
5 |
+
.replaceAll("]]", "]")
|
6 |
+
.replaceAll(",,", ",")
|
7 |
+
)
|
8 |
+
}
|
src/types.ts
CHANGED
@@ -78,3 +78,5 @@ export interface ImageAnalysisResponse {
|
|
78 |
result: string
|
79 |
error?: string
|
80 |
}
|
|
|
|
|
|
78 |
result: string
|
79 |
error?: string
|
80 |
}
|
81 |
+
|
82 |
+
export type LLMResponse = Array<{panel: number; caption: string }>
|