Skip to content

Commit dacfd8f

Browse files
refactor: convert settings.js to .mjs, class-ify cache.mjs, disk load in CachedInferenceEngine [opencode:big-pickle]
1 parent 38f8bb6 commit dacfd8f

6 files changed

Lines changed: 111 additions & 99 deletions

File tree

app/cached-inference-engine.mjs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,26 @@ export default class CachedInferenceEngine extends InferenceEngine {
77
}
88

99
async loadCache(url) {
10-
const response = await fetch(url)
11-
if (!response.ok) {
12-
throw new Error(`HTTP error! status: ${response.status}`)
10+
let ret = null
11+
const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null
12+
const urlStr = typeof url === 'string' ? url : url.href
13+
if (isNode) {
14+
if (!urlStr.startsWith('http:') && !urlStr.startsWith('https:')) {
15+
const { readFileSync } = await import('node:fs')
16+
const { fileURLToPath } = await import('node:url')
17+
const filePath = urlStr.startsWith('file:') ? fileURLToPath(urlStr) : urlStr
18+
ret = JSON.parse(readFileSync(filePath, 'utf-8'))
19+
}
20+
}
21+
if (ret === null) {
22+
const response = await fetch(url)
23+
if (!response.ok) {
24+
throw new Error(`HTTP error! status: ${response.status}`)
25+
}
26+
ret = await response.json()
1327
}
14-
this.cache = await response.json()
28+
this.cache = ret
29+
return ret
1530
}
1631

1732
getCache() {

app/nlpui.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ <h2 class="title is-3">Defendant's statement (with LLM's highlights)</h2>
159159
</div>
160160
</div>
161161
</section>
162-
<script src="settings.js"></script>
162+
<script type="module" src="settings.mjs"></script>
163163
<script type="module" src="nlpui.js"></script>
164164
</body>
165165
</html>

app/poc3.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ <h2 class="title is-3">Case</h2>
101101
</div>
102102
</div>
103103
</section>
104-
<script src="settings.js"></script>
104+
<script type="module" src="settings.mjs"></script>
105105
<script type="module" src="poc3.js"></script>
106106
</body>
107107
</html>

app/poc3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ DONE case selection
55
DONE show case
66
DONE show penalty
77
DONE table
8-
M draw LLM responses from cache
8+
DONE draw LLM responses from cache
99
S explaination
1010
C highlights
1111
C summary

app/settings.js renamed to app/settings.mjs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,6 @@ const SETTINGS = {
166166
},
167167
}
168168

169-
window.SETTINGS = SETTINGS
169+
if (typeof window !== 'undefined') {
170+
window.SETTINGS = SETTINGS
171+
}

tools/cache.mjs

Lines changed: 86 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,111 @@
11
#!/usr/bin/env node
22
import { readFileSync, writeFileSync } from 'node:fs'
3-
import { DEFAULT_TEMPLATE_QUESTIONNAIRE as TEMPLATE } from '../app/settings.js'
3+
import { DEFAULT_TEMPLATE_QUESTIONNAIRE as TEMPLATE } from '../app/settings.mjs'
4+
import CachedInferenceEngine from '../app/cached-inference-engine.mjs'
45

5-
const CACHE_FILE = new URL('../app/data/responses.json', import.meta.url)
66
const APP_DATA_FILE = new URL('../app/data/app-data.json', import.meta.url)
77

8-
function load() {
9-
return JSON.parse(readFileSync(CACHE_FILE, 'utf-8'))
10-
}
11-
12-
function actionLs() {
13-
const data = load()
8+
class ResponseCacheManager {
149

15-
for (const [key, entry] of Object.entries(data)) {
16-
console.log(`${key}\t${entry.model}\t${entry.response.length}`)
10+
constructor() {
11+
this.engine = new CachedInferenceEngine({
12+
serviceUrl: process.env.AJ_LLM_API,
13+
model: process.env.AJ_MODEL
14+
})
1715
}
18-
}
1916

20-
function actionModels() {
21-
const data = load()
22-
const counts = {}
23-
24-
for (const entry of Object.values(data)) {
25-
counts[entry.model] = (counts[entry.model] || 0) + 1
17+
async loadCache() {
18+
await this.engine.loadCache('../app/data/responses.json')
2619
}
2720

28-
for (const [model, count] of Object.entries(counts)) {
29-
console.log(`${model}\t${count}`)
21+
getCacheEntries() {
22+
return this.engine.getCache()
3023
}
31-
}
32-
33-
function hash(string) {
34-
let h = 0
35-
for (const char of string) {
36-
h = (h << 5) - h + char.charCodeAt(0)
37-
h |= 0
38-
}
39-
return h
40-
}
41-
42-
function buildPrompt(template, variables) {
43-
let ret = template
44-
for (const [k, v] of Object.entries(variables)) {
45-
ret = ret.replace(`{${k}}`, v)
46-
}
47-
return ret
48-
}
4924

50-
async function actionFetch() {
51-
const serviceUrl = process.env.AJ_LLM_API
52-
const model = process.env.AJ_MODEL
25+
actionLs() {
26+
const entries = this.getCacheEntries()
5327

54-
if (!serviceUrl || !model) {
55-
console.error('Set AJ_LLM_API and AJ_MODEL environment variables')
56-
process.exit(1)
28+
for (const [key, entry] of Object.entries(entries)) {
29+
console.log(`${key}\t${entry.model}\t${entry.response.length}`)
30+
}
5731
}
5832

59-
const cache = load()
60-
const appData = JSON.parse(readFileSync(APP_DATA_FILE, 'utf-8'))
61-
62-
for (const [caseKey, caseData] of Object.entries(appData.cases)) {
63-
for (const question of appData.questions) {
64-
const prompt = buildPrompt(TEMPLATE, {
65-
STATEMENT: caseData.statement,
66-
QUESTION: question.text,
67-
})
33+
actionModels() {
34+
const entries = this.getCacheEntries()
35+
const counts = {}
6836

69-
const key = hash(`${model}-${prompt}`)
37+
for (const entry of Object.values(entries)) {
38+
counts[entry.model] = (counts[entry.model] || 0) + 1
39+
}
7040

71-
if (cache[key]) {
72-
continue
73-
}
41+
for (const [model, count] of Object.entries(counts)) {
42+
console.log(`${model}\t${count}`)
43+
}
44+
}
7445

75-
const url = serviceUrl.replace(/\/+$/, '') + '/chat/completions'
76-
const res = await fetch(url, {
77-
method: 'POST',
78-
headers: { 'Content-Type': 'application/json' },
79-
body: JSON.stringify({
80-
model,
81-
messages: [{ role: 'user', content: prompt }],
82-
stream: false,
83-
max_tokens: 4000,
84-
}),
85-
})
86-
87-
if (!res.ok) {
88-
console.error(`API error (${res.status}) for ${caseKey} / ${question.text}`)
89-
continue
46+
async actionFetch() {
47+
const cache = this.engine.getCache()
48+
const appData = JSON.parse(readFileSync(APP_DATA_FILE, 'utf-8'))
49+
50+
for (const [caseKey, caseData] of Object.entries(appData.cases)) {
51+
for (const question of appData.questions) {
52+
const prompt = this.engine.getPromptFromTemplate(TEMPLATE, {
53+
STATEMENT: caseData.statement,
54+
QUESTION: question.text,
55+
})
56+
57+
const key = CachedInferenceEngine.hash(`${model}-${prompt}`)
58+
59+
if (cache[key]) {
60+
continue
61+
}
62+
63+
const url = serviceUrl.replace(/\/+$/, '') + '/chat/completions'
64+
const res = await fetch(url, {
65+
method: 'POST',
66+
headers: { 'Content-Type': 'application/json' },
67+
body: JSON.stringify({
68+
model,
69+
messages: [{ role: 'user', content: prompt }],
70+
stream: false,
71+
max_tokens: 4000,
72+
}),
73+
})
74+
75+
if (!res.ok) {
76+
console.error(`API error (${res.status}) for ${caseKey} / ${question.text}`)
77+
continue
78+
}
79+
80+
const data = await res.json()
81+
const response = data?.choices?.[0]?.message?.content
82+
83+
if (response) {
84+
cache[key] = { response, model, hash: key }
85+
console.log(`cached ${caseKey} / ${question.text}`)
86+
}
9087
}
88+
}
89+
}
9190

92-
const data = await res.json()
93-
const response = data?.choices?.[0]?.message?.content
94-
95-
if (response) {
96-
cache[key] = { response, model, hash: key }
97-
console.log(`cached ${caseKey} / ${question.text}`)
91+
async runAction(action) {
92+
await this.loadCache()
93+
94+
if (action === 'ls') {
95+
this.actionLs()
96+
} else if (action === 'models') {
97+
this.actionModels()
98+
} else if (action === 'fetch') {
99+
this.actionFetch()
100+
} else {
101+
if (action && action !== 'help') {
102+
console.error('Unknown action:', action)
98103
}
99-
100-
writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2))
104+
console.error('Usage: node cache.mjs <ls|models|fetch>')
105+
process.exit(1)
101106
}
102107
}
103108
}
104109

105-
const action = process.argv[2]
106-
if (action === 'ls') {
107-
actionLs()
108-
} else if (action === 'models') {
109-
actionModels()
110-
} else if (action === 'fetch') {
111-
actionFetch()
112-
} else {
113-
console.error('Unknown action:', action)
114-
console.error('Usage: node cache.mjs <ls|models|fetch>')
115-
process.exit(1)
116-
}
110+
const app = new ResponseCacheManager()
111+
await app.runAction(process.argv[2])

0 commit comments

Comments
 (0)