@gotcos/glasses-server 6.53.4 → 6.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +15 -0
- package/CHANGELOG.md +27 -0
- package/README.md +70 -0
- package/managed-runtime-contract.json +4 -1
- package/package.json +1 -1
- package/server/index.ts +7 -0
- package/server/lib/lens-gist-engines.ts +416 -0
- package/server/lib/lens-gist.ts +987 -0
- package/server/lib/provider-proof.ts +4 -2
- package/server/routes/health.ts +3 -0
- package/server/routes/lens-gist.ts +58 -0
|
@@ -194,7 +194,9 @@ function safeProofError(result: ProcessResult): string {
|
|
|
194
194
|
return 'provider returned no valid proof response'
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
/** A provider's own words for a spent session, usage, rate or capacity limit. Also the lens
|
|
198
|
+
* gist's (lens-gist-engines.ts), so both read a limit the same way. */
|
|
199
|
+
export const PROVIDER_QUOTA_RE = /usage[ _]limit|hit your (?:usage |session )?limit|session limit|rate.?limit|too many requests|\b429\b|\bquota\b|resets? (?:at|in) |over capacity|overloaded|\b529\b|plan limit/i
|
|
198
200
|
const AUTH_RE = /not logged in|please (?:log ?in|sign in)|invalid api key|authentication|unauthori[sz]ed|\b401\b|\b403\b|login required|token (?:has )?expired|no credentials/i
|
|
199
201
|
const OVERFLOW_RE = /prompt is too long|context window|too many tokens|exceeds the (?:model|context)/i
|
|
200
202
|
const MISSING_RE = /ENOENT|command not found|no such file/i
|
|
@@ -209,7 +211,7 @@ export function classifyProofFailure(result: ProcessResult, answer: string, expe
|
|
|
209
211
|
const haystack = `${result.stderr}\n${result.stdout}`.slice(0, 40_000)
|
|
210
212
|
if (result.code === null && MISSING_RE.test(haystack)) return 'provider_missing'
|
|
211
213
|
if (OVERFLOW_RE.test(haystack)) return 'provider_context_overflow'
|
|
212
|
-
if (
|
|
214
|
+
if (PROVIDER_QUOTA_RE.test(haystack)) return 'provider_quota'
|
|
213
215
|
if (AUTH_RE.test(haystack)) return 'provider_auth'
|
|
214
216
|
if (result.code === 0) return answer === expected ? 'provider_failed' : 'provider_bad_answer'
|
|
215
217
|
return 'provider_failed'
|
package/server/routes/health.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
} from '../lib/whisper-local.js'
|
|
23
23
|
import { getOpenAIWhisperBudgetState } from '../lib/openai-whisper-budget.js'
|
|
24
24
|
import { getMeetingSummaryBudgetState } from '../lib/meeting-summary-budget.js'
|
|
25
|
+
import { lensGistPublicHealth } from '../lib/lens-gist.js'
|
|
25
26
|
import { meetingSummaryLLMEnabled } from '../lib/meeting-summary.js'
|
|
26
27
|
import { getKeyStatus } from '../lib/openai-key.js'
|
|
27
28
|
import {
|
|
@@ -410,6 +411,8 @@ healthRouter.get('/health', async (_req, res) => {
|
|
|
410
411
|
whisper_health,
|
|
411
412
|
openai_whisper_budget,
|
|
412
413
|
meeting_summary,
|
|
414
|
+
// 6.54.0: the lens gist engine (lib/lens-gist.ts); detail on /api/lens-gist/config.
|
|
415
|
+
lens_gist: lensGistPublicHealth(),
|
|
413
416
|
tts_local,
|
|
414
417
|
codex_models,
|
|
415
418
|
cursor_models,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Router } from 'express'
|
|
2
|
+
import {
|
|
3
|
+
LensGistInputError,
|
|
4
|
+
getLensGist,
|
|
5
|
+
lensGistHealth,
|
|
6
|
+
listLensGistEngines,
|
|
7
|
+
normalizeLensGistInput,
|
|
8
|
+
saveLensGistConfig,
|
|
9
|
+
} from '../lib/lens-gist.js'
|
|
10
|
+
import { maintenanceAdmissionsOpen } from '../lib/maintenance-lifecycle.js'
|
|
11
|
+
import { errMsg } from '../lib/utils.js'
|
|
12
|
+
|
|
13
|
+
// Server 6.54.0. The phone asks for the three-line card behind the G2 prompt box
|
|
14
|
+
// (lib/lens-gist.ts). Every answer the server gives is a 200 with a `status`, so the
|
|
15
|
+
// phone never reads "the server answered no" as "the server is unreachable" (gotchas:
|
|
16
|
+
// an HTTP status means the server ANSWERED). Only a malformed request is a 400.
|
|
17
|
+
export const lensGistRouter = Router()
|
|
18
|
+
|
|
19
|
+
lensGistRouter.post('/lens-gist', async (req, res) => {
|
|
20
|
+
let input
|
|
21
|
+
try {
|
|
22
|
+
input = normalizeLensGistInput(req.body)
|
|
23
|
+
} catch (err) {
|
|
24
|
+
res.status(err instanceof LensGistInputError ? err.status : 400).json({ error: errMsg(err) })
|
|
25
|
+
return
|
|
26
|
+
}
|
|
27
|
+
// Outside the request lease (index.ts lifecycleOwned): a drain for an update or restart
|
|
28
|
+
// closes admissions, and a gist is the first thing to give way.
|
|
29
|
+
if (!maintenanceAdmissionsOpen()) {
|
|
30
|
+
res.json({ status: 'unavailable', reason: 'maintenance' })
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
// No abort on disconnect: the phone asks ahead of the hold and may not wait, and the
|
|
34
|
+
// answer is cached for when it asks again.
|
|
35
|
+
try {
|
|
36
|
+
res.json(await getLensGist(input))
|
|
37
|
+
} catch (err) {
|
|
38
|
+
res.status(500).json({ error: errMsg(err) })
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
lensGistRouter.get('/lens-gist/config', async (_req, res) => {
|
|
43
|
+
try {
|
|
44
|
+
res.json({ ...lensGistHealth(), engines: await listLensGistEngines() })
|
|
45
|
+
} catch (err) {
|
|
46
|
+
res.status(500).json({ error: errMsg(err) })
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
lensGistRouter.put('/lens-gist/config', (req, res) => {
|
|
51
|
+
try {
|
|
52
|
+
const saved = saveLensGistConfig(req.body ?? {})
|
|
53
|
+
const overridden = !!process.env.COS_LENS_GIST_ENGINE?.trim()
|
|
54
|
+
res.json({ saved: true, effective: saved, ...(overridden ? { overriddenByEnv: 'COS_LENS_GIST_ENGINE' } : {}) })
|
|
55
|
+
} catch (err) {
|
|
56
|
+
res.status(err instanceof LensGistInputError ? err.status : 500).json({ error: errMsg(err) })
|
|
57
|
+
}
|
|
58
|
+
})
|