vibeoscore 1.0.2
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 +5 -0
- package/README.md +29 -0
- package/client.js +257 -0
- package/client.ts +334 -0
- package/dashboard/dist/assets/index-BnPt1Fii.js +1 -0
- package/dashboard/dist/assets/index-CfH00tOL.css +1 -0
- package/dashboard/dist/index.html +3 -0
- package/lib/blackbox-rf.js +1099 -0
- package/lib/blackbox.js +137 -0
- package/lib/compression.js +119 -0
- package/lib/db.js +106 -0
- package/lib/db.ts +113 -0
- package/lib/delegation.js +137 -0
- package/lib/meta-controller.js +418 -0
- package/lib/meta-controller.mjs +499 -0
- package/lib/patterns.js +150 -0
- package/lib/resolution-tracker.js +486 -0
- package/lib/stress.js +84 -0
- package/lib/tdd.js +218 -0
- package/lib/tier-routing.js +48 -0
- package/mcp-server.js +370 -0
- package/mcp-server.ts +364 -0
- package/middleware/auth.js +75 -0
- package/middleware/auth.ts +87 -0
- package/middleware/usage-logging.js +29 -0
- package/middleware/usage-logging.ts +41 -0
- package/nginx-vibetheog-api.conf +64 -0
- package/package.json +66 -0
- package/routes/admin.js +93 -0
- package/routes/admin.ts +107 -0
- package/routes/blackbox.js +463 -0
- package/routes/compression.js +12 -0
- package/routes/delegation.js +30 -0
- package/routes/patterns.js +53 -0
- package/routes/pricing.js +62 -0
- package/routes/stress.js +30 -0
- package/routes/tdd.js +68 -0
- package/routes/tier-routing.js +31 -0
- package/scripts/dashboard-server.mjs +246 -0
- package/scripts/deploy-zero-downtime.sh +77 -0
- package/scripts/deploy.sh +68 -0
- package/scripts/release.mjs +30 -0
- package/scripts/seed-master-token.js +29 -0
- package/scripts/start-all.mjs +34 -0
- package/server.js +88 -0
- package/vibeos-api.service +19 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync } from "node:fs"
|
|
2
|
+
import { dirname, resolve } from "node:path"
|
|
3
|
+
import { fileURLToPath } from "node:url"
|
|
4
|
+
import { ResolutionTracker, SUB_REGIMES, extractFeatures } from "../lib/blackbox.js"
|
|
5
|
+
import { computeControlVector } from "../lib/meta-controller.js"
|
|
6
|
+
import { getDb } from "../lib/db.js"
|
|
7
|
+
import {
|
|
8
|
+
getBlackboxRoutingModelMeta,
|
|
9
|
+
rebuildBlackboxRoutingModel,
|
|
10
|
+
selectBlackboxMode,
|
|
11
|
+
} from "../lib/blackbox-rf.js"
|
|
12
|
+
|
|
13
|
+
const trackers = new Map()
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
15
|
+
const TELEMETRY_PATH = process.env.VIBEOS_BLACKBOX_TELEMETRY_PATH || resolve(__dirname, "..", "data", "blackbox-telemetry.jsonl")
|
|
16
|
+
|
|
17
|
+
function readSessionStateRow(sessionId) {
|
|
18
|
+
try {
|
|
19
|
+
const db = getDb()
|
|
20
|
+
return db.prepare("SELECT state_json FROM blackbox_sessions WHERE session_id = ? ORDER BY updated_at DESC LIMIT 1").get(sessionId) || null
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error(`[blackbox] readSessionStateRow failed for ${sessionId}: ${err.message}`)
|
|
23
|
+
return null
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function parseStateJson(row) {
|
|
28
|
+
if (!row?.state_json) return null
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(row.state_json)
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return null
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function loadTrackerFromDb(sessionId) {
|
|
37
|
+
try {
|
|
38
|
+
const data = parseStateJson(readSessionStateRow(sessionId))
|
|
39
|
+
if (data) {
|
|
40
|
+
return ResolutionTracker.deserialize(data)
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error(`[blackbox] loadTrackerFromDb failed for ${sessionId}: ${err.message}`)
|
|
44
|
+
}
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function appendTelemetryLog(record) {
|
|
49
|
+
try {
|
|
50
|
+
mkdirSync(dirname(TELEMETRY_PATH), { recursive: true })
|
|
51
|
+
appendFileSync(TELEMETRY_PATH, `${JSON.stringify(record)}\n`, "utf-8")
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(`[blackbox] appendTelemetryLog failed: ${err.message}`)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function saveTrackerToDb(sessionId, projectId, tracker, telemetry = null) {
|
|
58
|
+
try {
|
|
59
|
+
const db = getDb()
|
|
60
|
+
const existing = parseStateJson(readSessionStateRow(sessionId))
|
|
61
|
+
const serialized = tracker.serialize()
|
|
62
|
+
const mergedTelemetry = {
|
|
63
|
+
...(existing?.telemetry && typeof existing.telemetry === "object" ? existing.telemetry : {}),
|
|
64
|
+
...(telemetry && typeof telemetry === "object" ? telemetry : {}),
|
|
65
|
+
}
|
|
66
|
+
const payload = {
|
|
67
|
+
...(existing && typeof existing === "object" ? existing : {}),
|
|
68
|
+
...serialized,
|
|
69
|
+
}
|
|
70
|
+
if (Object.keys(mergedTelemetry).length > 0) {
|
|
71
|
+
payload.telemetry = mergedTelemetry
|
|
72
|
+
}
|
|
73
|
+
const stateJson = JSON.stringify(payload)
|
|
74
|
+
const outcome = tracker.getOutcomeHistory().slice(-1)[0]?.outcome || null
|
|
75
|
+
const now = new Date().toISOString()
|
|
76
|
+
db.prepare(`
|
|
77
|
+
INSERT INTO blackbox_sessions (session_id, project_id, state_json, outcome, created_at, updated_at)
|
|
78
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
79
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
80
|
+
state_json = excluded.state_json,
|
|
81
|
+
project_id = COALESCE(excluded.project_id, blackbox_sessions.project_id),
|
|
82
|
+
outcome = COALESCE(excluded.outcome, blackbox_sessions.outcome),
|
|
83
|
+
updated_at = excluded.updated_at
|
|
84
|
+
`).run(sessionId, projectId || null, stateJson, outcome, now, now)
|
|
85
|
+
if (Object.keys(mergedTelemetry).length > 0) {
|
|
86
|
+
appendTelemetryLog({
|
|
87
|
+
session_id: sessionId,
|
|
88
|
+
project_id: projectId || null,
|
|
89
|
+
event: "session_save",
|
|
90
|
+
created_at: now,
|
|
91
|
+
telemetry: mergedTelemetry,
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error(`[blackbox] saveTrackerToDb failed for ${sessionId}: ${err.message}`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getOrCreateTracker(sessionId, projectId) {
|
|
100
|
+
let tracker = trackers.get(sessionId)
|
|
101
|
+
if (!tracker) {
|
|
102
|
+
tracker = loadTrackerFromDb(sessionId) || new ResolutionTracker(sessionId, projectId)
|
|
103
|
+
trackers.set(sessionId, tracker)
|
|
104
|
+
}
|
|
105
|
+
if (projectId && !tracker.projectId) {
|
|
106
|
+
tracker.projectId = projectId
|
|
107
|
+
}
|
|
108
|
+
return tracker
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function blackboxRoutes(fastify) {
|
|
112
|
+
fastify.post("/api/v1/blackbox/analyze", async (request, reply) => {
|
|
113
|
+
const {
|
|
114
|
+
session_id,
|
|
115
|
+
project_id,
|
|
116
|
+
user_text,
|
|
117
|
+
prompt,
|
|
118
|
+
features,
|
|
119
|
+
action,
|
|
120
|
+
entropy,
|
|
121
|
+
uncertainty,
|
|
122
|
+
embedding,
|
|
123
|
+
latest_stress_multiplier,
|
|
124
|
+
stress_multiplier,
|
|
125
|
+
outcome,
|
|
126
|
+
} = request.body || {}
|
|
127
|
+
|
|
128
|
+
if (!user_text && !features && !action) {
|
|
129
|
+
return reply.code(400).send({ error: "user_text is required" })
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const sid = session_id || "default"
|
|
133
|
+
const tracker = getOrCreateTracker(sid, project_id)
|
|
134
|
+
|
|
135
|
+
const derivedFeatures = typeof features === "object" && !Array.isArray(features) && Object.keys(features || {}).length > 0
|
|
136
|
+
? features
|
|
137
|
+
: extractFeatures(user_text)
|
|
138
|
+
|
|
139
|
+
const state = tracker.update({
|
|
140
|
+
userText: user_text || "",
|
|
141
|
+
features: derivedFeatures,
|
|
142
|
+
actions: typeof action === "string" ? [action] : (Array.isArray(action) ? action : []),
|
|
143
|
+
entropy: entropy ?? 1.0,
|
|
144
|
+
uncertainty: uncertainty ?? 50,
|
|
145
|
+
embedding: embedding || null,
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
const selectionInput = {
|
|
149
|
+
session_id: sid,
|
|
150
|
+
project_id: project_id || tracker.projectId || null,
|
|
151
|
+
user_text: user_text || "",
|
|
152
|
+
prompt: prompt || "",
|
|
153
|
+
features: derivedFeatures,
|
|
154
|
+
state,
|
|
155
|
+
pivot_detected: state.pivot_detected,
|
|
156
|
+
pivot_score: state.pivot_score,
|
|
157
|
+
resolution: state.resolution,
|
|
158
|
+
continuity_state: state.continuity_state,
|
|
159
|
+
is_looping: state.is_looping,
|
|
160
|
+
loop_count: state.loop_consecutive,
|
|
161
|
+
loop_consecutive: state.loop_consecutive,
|
|
162
|
+
loop_intervention_level: state.loop_intervention_level,
|
|
163
|
+
outcome: outcome || state.outcome || null,
|
|
164
|
+
signals: state.signals,
|
|
165
|
+
stress_multiplier: latest_stress_multiplier ?? stress_multiplier ?? 0,
|
|
166
|
+
latest_stress_multiplier: latest_stress_multiplier ?? stress_multiplier ?? 0,
|
|
167
|
+
}
|
|
168
|
+
const selected = selectBlackboxMode(selectionInput)
|
|
169
|
+
const controlVector = computeControlVector({
|
|
170
|
+
...state,
|
|
171
|
+
user_text: user_text || "",
|
|
172
|
+
prompt: prompt || "",
|
|
173
|
+
features: derivedFeatures,
|
|
174
|
+
latest_stress_multiplier: latest_stress_multiplier ?? stress_multiplier ?? 0,
|
|
175
|
+
}, action, selected.mode)
|
|
176
|
+
const telemetry = {
|
|
177
|
+
observed_at: new Date().toISOString(),
|
|
178
|
+
input: {
|
|
179
|
+
user_text: user_text || "",
|
|
180
|
+
prompt: prompt || "",
|
|
181
|
+
action: typeof action === "string" ? action : Array.isArray(action) ? action[0] || null : null,
|
|
182
|
+
entropy: entropy ?? 1.0,
|
|
183
|
+
uncertainty: uncertainty ?? 50,
|
|
184
|
+
latest_stress_multiplier: latest_stress_multiplier ?? stress_multiplier ?? 0,
|
|
185
|
+
},
|
|
186
|
+
selection: {
|
|
187
|
+
optimization_mode: selected.mode,
|
|
188
|
+
source: selected.source,
|
|
189
|
+
confidence: selected.confidence,
|
|
190
|
+
probabilities: selected.probabilities || {},
|
|
191
|
+
},
|
|
192
|
+
context_packet: controlVector.context_packet,
|
|
193
|
+
control_vector: controlVector,
|
|
194
|
+
signals: {
|
|
195
|
+
sub_regime: state.sub_regime,
|
|
196
|
+
resolution: state.resolution,
|
|
197
|
+
continuity_state: state.continuity_state,
|
|
198
|
+
pivot_detected: state.pivot_detected,
|
|
199
|
+
pivot_score: state.pivot_score,
|
|
200
|
+
is_looping: state.is_looping,
|
|
201
|
+
loop_intervention_level: state.loop_intervention_level,
|
|
202
|
+
loop_consecutive: state.loop_consecutive,
|
|
203
|
+
action_consistency: state.signals?.action_consistency,
|
|
204
|
+
n_interactions: state.n_interactions,
|
|
205
|
+
outcome: outcome || state.outcome || null,
|
|
206
|
+
},
|
|
207
|
+
features: derivedFeatures,
|
|
208
|
+
feature_vector: selected.features || [],
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
tracker.annotateLastTurn(telemetry)
|
|
212
|
+
saveTrackerToDb(sid, project_id, tracker, telemetry)
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
...state,
|
|
216
|
+
session_id: sid,
|
|
217
|
+
project_id: project_id || tracker.projectId || null,
|
|
218
|
+
features: derivedFeatures,
|
|
219
|
+
optimization_mode: selected.mode,
|
|
220
|
+
optimization_source: selected.source,
|
|
221
|
+
optimization_confidence: selected.confidence,
|
|
222
|
+
context_packet: controlVector.context_packet,
|
|
223
|
+
control_vector: controlVector,
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
fastify.post("/api/v1/blackbox/state", async (request, reply) => {
|
|
228
|
+
const { session_id, project_id } = request.body || {}
|
|
229
|
+
const sid = session_id || "default"
|
|
230
|
+
const tracker = getOrCreateTracker(sid, project_id)
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
...tracker.getState(),
|
|
234
|
+
session_id: sid,
|
|
235
|
+
project_id: project_id || tracker.projectId || null,
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
fastify.post("/api/v1/blackbox/reset", async (request, reply) => {
|
|
240
|
+
const { session_id } = request.body || {}
|
|
241
|
+
const sid = session_id || "default"
|
|
242
|
+
trackers.delete(sid)
|
|
243
|
+
try {
|
|
244
|
+
getDb().prepare("DELETE FROM blackbox_sessions WHERE session_id = ?").run(sid)
|
|
245
|
+
} catch {}
|
|
246
|
+
return { ok: true, message: "tracker reset" }
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
fastify.get("/api/v1/blackbox/regimes", async (request, reply) => {
|
|
250
|
+
return { regimes: SUB_REGIMES }
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
fastify.get("/api/v1/blackbox/project-sessions", async (request, reply) => {
|
|
254
|
+
const { project_id } = request.query || {}
|
|
255
|
+
if (!project_id) {
|
|
256
|
+
return reply.code(400).send({ error: "project_id query parameter is required" })
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
const db = getDb()
|
|
260
|
+
const rows = db.prepare(
|
|
261
|
+
"SELECT session_id, created_at, updated_at, outcome FROM blackbox_sessions WHERE project_id = ? ORDER BY updated_at DESC LIMIT 50"
|
|
262
|
+
).all(project_id)
|
|
263
|
+
return { project_id, sessions: rows }
|
|
264
|
+
} catch (err) {
|
|
265
|
+
return reply.code(500).send({ error: "failed to query sessions" })
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
fastify.post("/api/v1/blackbox/outcome", async (request, reply) => {
|
|
270
|
+
const { session_id, outcome } = request.body || {}
|
|
271
|
+
if (!session_id || !outcome) {
|
|
272
|
+
return reply.code(400).send({ error: "session_id and outcome are required" })
|
|
273
|
+
}
|
|
274
|
+
const sid = session_id
|
|
275
|
+
const tracker = trackers.get(sid) || loadTrackerFromDb(sid)
|
|
276
|
+
if (tracker) {
|
|
277
|
+
tracker.recordOutcome(outcome)
|
|
278
|
+
tracker.annotateLastTurn({
|
|
279
|
+
observed_at: new Date().toISOString(),
|
|
280
|
+
outcome,
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const db = getDb()
|
|
285
|
+
db.prepare("UPDATE blackbox_sessions SET outcome = ?, updated_at = ? WHERE session_id = ?")
|
|
286
|
+
.run(outcome, new Date().toISOString(), sid)
|
|
287
|
+
if (tracker) {
|
|
288
|
+
saveTrackerToDb(sid, tracker.projectId || null, tracker, {
|
|
289
|
+
observed_at: new Date().toISOString(),
|
|
290
|
+
outcome,
|
|
291
|
+
})
|
|
292
|
+
}
|
|
293
|
+
} catch {}
|
|
294
|
+
return { ok: true, session_id: sid, outcome }
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
fastify.post("/api/v1/blackbox/calibrate", async (request, reply) => {
|
|
298
|
+
const { project_id } = request.body || {}
|
|
299
|
+
const pid = project_id || "global"
|
|
300
|
+
try {
|
|
301
|
+
const db = getDb()
|
|
302
|
+
const sessions = db.prepare(
|
|
303
|
+
"SELECT state_json, outcome FROM blackbox_sessions WHERE project_id = ? AND outcome IS NOT NULL"
|
|
304
|
+
).all(pid)
|
|
305
|
+
|
|
306
|
+
if (sessions.length < 3) {
|
|
307
|
+
return reply.code(400).send({
|
|
308
|
+
error: "need at least 3 sessions with outcomes to calibrate",
|
|
309
|
+
samples: sessions.length,
|
|
310
|
+
})
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const outcomes = sessions.map(s => JSON.parse(s.state_json))
|
|
314
|
+
const loopSessions = outcomes.filter(o => {
|
|
315
|
+
try {
|
|
316
|
+
const state = typeof o === "string" ? JSON.parse(o) : o
|
|
317
|
+
return state?.is_looping
|
|
318
|
+
} catch { return false }
|
|
319
|
+
})
|
|
320
|
+
const positiveOutcomes = sessions.filter(s => s.outcome === "positive").length
|
|
321
|
+
const total = sessions.length
|
|
322
|
+
|
|
323
|
+
const weights = {
|
|
324
|
+
momentum: [-0.3, 0.5, 0.2],
|
|
325
|
+
subRegime: {
|
|
326
|
+
CONVERGING: -0.10,
|
|
327
|
+
CLOSED: -0.10,
|
|
328
|
+
DIVERGENT: +0.15,
|
|
329
|
+
REFINING: 0.00,
|
|
330
|
+
EXPLORING: +0.10,
|
|
331
|
+
LOOPING: +0.15,
|
|
332
|
+
INIT: +0.05,
|
|
333
|
+
},
|
|
334
|
+
loopJaccard: loopSessions.length > 0 ? 0.6 - (loopSessions.length / total) * 0.1 : 0.6,
|
|
335
|
+
closureConfidence: positiveOutcomes > 0 ? 0.7 - (positiveOutcomes / total) * 0.1 : 0.7,
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
db.prepare(`
|
|
339
|
+
INSERT INTO blackbox_calibration (project_id, weights_json, samples_used, updated_at)
|
|
340
|
+
VALUES (?, ?, ?, ?)
|
|
341
|
+
ON CONFLICT(project_id) DO UPDATE SET
|
|
342
|
+
weights_json = excluded.weights_json,
|
|
343
|
+
samples_used = excluded.samples_used,
|
|
344
|
+
updated_at = excluded.updated_at
|
|
345
|
+
`).run(pid, JSON.stringify(weights), total, new Date().toISOString())
|
|
346
|
+
|
|
347
|
+
const tracker = [...trackers.values()].find(t => t.projectId === pid)
|
|
348
|
+
if (tracker) {
|
|
349
|
+
tracker.setCalibratedWeights(weights)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return { ok: true, project_id: pid, samples: total, weights }
|
|
353
|
+
} catch (err) {
|
|
354
|
+
return reply.code(500).send({ error: `calibration failed: ${err.message}` })
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
|
|
358
|
+
fastify.get("/api/v1/blackbox/calibration", async (request, reply) => {
|
|
359
|
+
const { project_id } = request.query || {}
|
|
360
|
+
const pid = project_id || "global"
|
|
361
|
+
try {
|
|
362
|
+
const db = getDb()
|
|
363
|
+
const row = db.prepare(
|
|
364
|
+
"SELECT weights_json, samples_used, updated_at FROM blackbox_calibration WHERE project_id = ?"
|
|
365
|
+
).get(pid)
|
|
366
|
+
if (!row) {
|
|
367
|
+
return { project_id: pid, calibrated: false, message: "no calibration data yet" }
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
project_id: pid,
|
|
371
|
+
calibrated: true,
|
|
372
|
+
weights: JSON.parse(row.weights_json),
|
|
373
|
+
samples_used: row.samples_used,
|
|
374
|
+
updated_at: row.updated_at,
|
|
375
|
+
}
|
|
376
|
+
} catch (err) {
|
|
377
|
+
return reply.code(500).send({ error: `failed to read calibration: ${err.message}` })
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
fastify.post("/api/v1/blackbox/control-vector", async (request, reply) => {
|
|
382
|
+
const {
|
|
383
|
+
sub_regime,
|
|
384
|
+
is_looping,
|
|
385
|
+
loop_intervention_level,
|
|
386
|
+
momentum,
|
|
387
|
+
n_interactions,
|
|
388
|
+
latest_stress_multiplier,
|
|
389
|
+
action,
|
|
390
|
+
optimization_mode,
|
|
391
|
+
user_text,
|
|
392
|
+
prompt,
|
|
393
|
+
features,
|
|
394
|
+
pivot_detected,
|
|
395
|
+
pivot_score,
|
|
396
|
+
resolution,
|
|
397
|
+
continuity_state,
|
|
398
|
+
outcome,
|
|
399
|
+
signals,
|
|
400
|
+
} = request.body || {}
|
|
401
|
+
const state = {
|
|
402
|
+
sub_regime,
|
|
403
|
+
is_looping,
|
|
404
|
+
loop_intervention_level,
|
|
405
|
+
momentum,
|
|
406
|
+
n_interactions,
|
|
407
|
+
latest_stress_multiplier,
|
|
408
|
+
user_text,
|
|
409
|
+
prompt,
|
|
410
|
+
features,
|
|
411
|
+
pivot_detected,
|
|
412
|
+
pivot_score,
|
|
413
|
+
resolution,
|
|
414
|
+
continuity_state,
|
|
415
|
+
outcome,
|
|
416
|
+
signals,
|
|
417
|
+
}
|
|
418
|
+
const cv = computeControlVector(state, action, optimization_mode)
|
|
419
|
+
return { ok: true, control_vector: cv }
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
fastify.post("/api/v1/blackbox/select-mode", async (request, reply) => {
|
|
423
|
+
const result = selectBlackboxMode(request.body || {})
|
|
424
|
+
return { ok: true, mode: result.mode, ...result }
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
fastify.get("/api/v1/blackbox/model", async () => {
|
|
428
|
+
return { ok: true, ...getBlackboxRoutingModelMeta() }
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
fastify.post("/api/v1/blackbox/model/rebuild", async (request, reply) => {
|
|
432
|
+
const {
|
|
433
|
+
project_id,
|
|
434
|
+
limit,
|
|
435
|
+
include_bootstrap,
|
|
436
|
+
seed,
|
|
437
|
+
tree_count,
|
|
438
|
+
max_depth,
|
|
439
|
+
min_samples_split,
|
|
440
|
+
min_samples_leaf,
|
|
441
|
+
feature_subsample,
|
|
442
|
+
confidence_threshold,
|
|
443
|
+
} = request.body || {}
|
|
444
|
+
|
|
445
|
+
const result = rebuildBlackboxRoutingModel({
|
|
446
|
+
projectId: project_id || null,
|
|
447
|
+
limit: Number.isFinite(Number(limit)) ? Number(limit) : 5000,
|
|
448
|
+
includeBootstrap: include_bootstrap !== false,
|
|
449
|
+
seed: Number.isFinite(Number(seed)) ? Number(seed) : 42,
|
|
450
|
+
treeCount: Number.isFinite(Number(tree_count)) ? Number(tree_count) : undefined,
|
|
451
|
+
maxDepth: Number.isFinite(Number(max_depth)) ? Number(max_depth) : undefined,
|
|
452
|
+
minSamplesSplit: Number.isFinite(Number(min_samples_split)) ? Number(min_samples_split) : undefined,
|
|
453
|
+
minSamplesLeaf: Number.isFinite(Number(min_samples_leaf)) ? Number(min_samples_leaf) : undefined,
|
|
454
|
+
featureSubsample: Number.isFinite(Number(feature_subsample)) ? Number(feature_subsample) : undefined,
|
|
455
|
+
confidenceThreshold: Number.isFinite(Number(confidence_threshold)) ? Number(confidence_threshold) : undefined,
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
if (!result.ok) {
|
|
459
|
+
return reply.code(400).send(result)
|
|
460
|
+
}
|
|
461
|
+
return result
|
|
462
|
+
})
|
|
463
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { compressToolOutput, COMPRESS_THRESHOLD } from "../lib/compression.js"
|
|
2
|
+
|
|
3
|
+
export async function compressionRoutes(fastify) {
|
|
4
|
+
fastify.post("/api/v1/compress/context", async (request, reply) => {
|
|
5
|
+
const { text, threshold } = request.body || {}
|
|
6
|
+
if (!text) {
|
|
7
|
+
return reply.code(400).send({ error: "text is required" })
|
|
8
|
+
}
|
|
9
|
+
const result = compressToolOutput(text, threshold || COMPRESS_THRESHOLD)
|
|
10
|
+
return result
|
|
11
|
+
})
|
|
12
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { checkDelegation, checkSoftQuota, modelCostPerTurn, normalizeModelId } from "../lib/delegation.js"
|
|
2
|
+
|
|
3
|
+
export async function delegationRoutes(fastify) {
|
|
4
|
+
fastify.post("/api/v1/delegate/check", async (request, reply) => {
|
|
5
|
+
const { tool, tier, model, prompt, dynamic_cache } = request.body || {}
|
|
6
|
+
if (!tool || !tier) {
|
|
7
|
+
return reply.code(400).send({ error: "tool and tier are required" })
|
|
8
|
+
}
|
|
9
|
+
const result = checkDelegation(tool, tier, model, prompt, dynamic_cache || {})
|
|
10
|
+
return result
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
fastify.post("/api/v1/delegate/soft-quota", async (request, reply) => {
|
|
14
|
+
const { tool, current_count, limit } = request.body || {}
|
|
15
|
+
if (!tool) {
|
|
16
|
+
return reply.code(400).send({ error: "tool is required" })
|
|
17
|
+
}
|
|
18
|
+
const result = checkSoftQuota(tool, current_count || 0, limit)
|
|
19
|
+
return result
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
fastify.post("/api/v1/delegation/cost", async (request, reply) => {
|
|
23
|
+
const { model, dynamic_cache } = request.body || {}
|
|
24
|
+
if (!model) {
|
|
25
|
+
return reply.code(400).send({ error: "model is required" })
|
|
26
|
+
}
|
|
27
|
+
const cost = modelCostPerTurn(model, dynamic_cache || {})
|
|
28
|
+
return { model: normalizeModelId(model), cost_per_turn: cost }
|
|
29
|
+
})
|
|
30
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PatternStore } from "../lib/patterns.js"
|
|
2
|
+
|
|
3
|
+
const stores = new Map()
|
|
4
|
+
|
|
5
|
+
function getStore(sessionId = "default") {
|
|
6
|
+
if (!stores.has(sessionId)) {
|
|
7
|
+
stores.set(sessionId, new PatternStore())
|
|
8
|
+
}
|
|
9
|
+
return stores.get(sessionId)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function patternRoutes(fastify) {
|
|
13
|
+
fastify.post("/api/v1/patterns/observe", async (request, reply) => {
|
|
14
|
+
const { session_id, tool_name, input, output, directory } = request.body || {}
|
|
15
|
+
if (!tool_name) {
|
|
16
|
+
return reply.code(400).send({ error: "tool_name is required" })
|
|
17
|
+
}
|
|
18
|
+
const store = getStore(session_id)
|
|
19
|
+
const patterns = store.observeToolEvent(tool_name, input, output, directory)
|
|
20
|
+
return { patterns_detected: patterns.length, patterns }
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
fastify.post("/api/v1/patterns/record", async (request, reply) => {
|
|
24
|
+
const { session_id, kind, key, summary, meta } = request.body || {}
|
|
25
|
+
if (!kind || !key || !summary) {
|
|
26
|
+
return reply.code(400).send({ error: "kind, key, and summary are required" })
|
|
27
|
+
}
|
|
28
|
+
const store = getStore(session_id)
|
|
29
|
+
const pattern = store.recordPattern(kind, key, summary, meta || {})
|
|
30
|
+
return pattern
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
fastify.get("/api/v1/patterns/query", async (request, reply) => {
|
|
34
|
+
const { session_id, kind } = request.query || {}
|
|
35
|
+
const store = getStore(session_id)
|
|
36
|
+
const patterns = store.getPatterns(kind || null)
|
|
37
|
+
return { patterns, count: patterns.length }
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
fastify.get("/api/v1/patterns/exploratory-words", async (request, reply) => {
|
|
41
|
+
const { session_id } = request.query || {}
|
|
42
|
+
const store = getStore(session_id)
|
|
43
|
+
const words = store.getLearnedExploratoryWords()
|
|
44
|
+
return { words }
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
fastify.post("/api/v1/patterns/clear", async (request, reply) => {
|
|
48
|
+
const { session_id } = request.body || {}
|
|
49
|
+
const store = getStore(session_id)
|
|
50
|
+
store.clear()
|
|
51
|
+
return { ok: true }
|
|
52
|
+
})
|
|
53
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { parseOpenRouterTurnCost, modelCostPerTurn, normalizeModelId, MODEL_USD_PER_TURN } from "../lib/delegation.js"
|
|
2
|
+
|
|
3
|
+
const pricingCache = new Map()
|
|
4
|
+
let lastFetch = 0
|
|
5
|
+
const CACHE_TTL = 10 * 60 * 1000
|
|
6
|
+
|
|
7
|
+
export async function pricingRoutes(fastify) {
|
|
8
|
+
fastify.post("/api/v1/pricing/fetch", async (request, reply) => {
|
|
9
|
+
const { openrouter_key, force } = request.body || {}
|
|
10
|
+
|
|
11
|
+
const now = Date.now()
|
|
12
|
+
if (!force && pricingCache.size > 0 && (now - lastFetch) < CACHE_TTL) {
|
|
13
|
+
return { cached: true, models: Object.fromEntries(pricingCache), count: pricingCache.size }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!openrouter_key) {
|
|
17
|
+
return reply.code(400).send({ error: "openrouter_key is required for live fetch" })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch("https://openrouter.ai/api/v1/models", {
|
|
22
|
+
headers: { Authorization: "Bearer " + openrouter_key },
|
|
23
|
+
signal: AbortSignal.timeout(10000),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
if (!res.ok) {
|
|
27
|
+
return reply.code(res.status).send({ error: "openrouter api error", status: res.status })
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const body = await res.json()
|
|
31
|
+
const list = body?.data || []
|
|
32
|
+
const pricingMap = {}
|
|
33
|
+
|
|
34
|
+
for (const m of list) {
|
|
35
|
+
const dynTurnCost = parseOpenRouterTurnCost(m)
|
|
36
|
+
if (dynTurnCost != null && Number.isFinite(dynTurnCost)) {
|
|
37
|
+
const rawId = m.id || ""
|
|
38
|
+
pricingMap[normalizeModelId(rawId)] = dynTurnCost
|
|
39
|
+
pricingCache.set(normalizeModelId(rawId), dynTurnCost)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
lastFetch = now
|
|
44
|
+
return { cached: false, models: pricingMap, count: Object.keys(pricingMap).length }
|
|
45
|
+
} catch (err) {
|
|
46
|
+
return reply.code(502).send({ error: "failed to fetch pricing", message: err.message })
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
fastify.post("/api/v1/pricing/lookup", async (request, reply) => {
|
|
51
|
+
const { model } = request.body || {}
|
|
52
|
+
if (!model) {
|
|
53
|
+
return reply.code(400).send({ error: "model is required" })
|
|
54
|
+
}
|
|
55
|
+
const cost = modelCostPerTurn(model, Object.fromEntries(pricingCache))
|
|
56
|
+
return { model: normalizeModelId(model), cost_per_turn: cost ?? null, in_cache: pricingCache.has(normalizeModelId(model)) }
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
fastify.get("/api/v1/pricing/static", async (request, reply) => {
|
|
60
|
+
return { models: MODEL_USD_PER_TURN, count: Object.keys(MODEL_USD_PER_TURN).length }
|
|
61
|
+
})
|
|
62
|
+
}
|
package/routes/stress.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { scoreStress, getStressLevel, buildStressFooter } from "../lib/stress.js"
|
|
2
|
+
|
|
3
|
+
export async function stressRoutes(fastify) {
|
|
4
|
+
fastify.post("/api/v1/stress/score", async (request, reply) => {
|
|
5
|
+
const { text } = request.body || {}
|
|
6
|
+
if (!text) {
|
|
7
|
+
return reply.code(400).send({ error: "text is required" })
|
|
8
|
+
}
|
|
9
|
+
const score = scoreStress(text)
|
|
10
|
+
const level = getStressLevel(score)
|
|
11
|
+
const footer = buildStressFooter(score)
|
|
12
|
+
return { score, level: level.level, gauge: level.gauge, directive: level.directive, footer }
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
fastify.post("/api/v1/stress/level", async (request, reply) => {
|
|
16
|
+
const { score } = request.body || {}
|
|
17
|
+
if (score === undefined || score === null) {
|
|
18
|
+
return reply.code(400).send({ error: "score is required" })
|
|
19
|
+
}
|
|
20
|
+
const numScore = Number(score)
|
|
21
|
+
if (!Number.isFinite(numScore)) {
|
|
22
|
+
return reply.code(400).send({ error: "score must be a finite number" })
|
|
23
|
+
}
|
|
24
|
+
if (numScore < 0) {
|
|
25
|
+
return reply.code(400).send({ error: "score must be non-negative" })
|
|
26
|
+
}
|
|
27
|
+
const level = getStressLevel(numScore)
|
|
28
|
+
return level
|
|
29
|
+
})
|
|
30
|
+
}
|