@konneal/engine 0.1.4 → 0.2.1
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/dist/admin.d.ts +1 -0
- package/dist/ask-47RNGK2R.js +12 -0
- package/dist/chunk-3OXSQH7Y.js +1852 -0
- package/dist/chunk-6GOSMLRH.js +2781 -0
- package/dist/{chunk-EHJEELVB.js → chunk-LNSDBEKS.js} +1 -1
- package/dist/{chunk-35ODH64W.js → chunk-Q327B27J.js} +33 -0
- package/dist/{chunk-OCNLV7Q7.js → chunk-Q6LI4T7M.js} +6 -1
- package/dist/{chunk-ROF3Q7UC.js → chunk-SN3ANQ3Y.js} +2 -2
- package/dist/chunk-VJZLVU3S.js +64 -0
- package/dist/{chunk-CAEHIVG5.js → chunk-WGXATDXY.js} +1 -1
- package/dist/codecs.d.ts +3 -3
- package/dist/completion.d.ts +1 -1
- package/dist/config.d.ts +9 -0
- package/dist/faithfulness.d.ts +1 -0
- package/dist/mcp-proto.d.ts +25 -0
- package/dist/mcp.d.ts +4 -0
- package/dist/openapi-surface.gen.d.ts +9 -0
- package/dist/openapi-types.d.ts +2141 -0
- package/dist/profile.gen.d.ts +1 -0
- package/dist/prompts/system.md +1 -0
- package/dist/quota.d.ts +4 -1
- package/dist/search-OMPBMZT4.js +11 -0
- package/dist/tablecontext.d.ts +7 -0
- package/dist/verdict-parse.d.ts +5 -0
- package/dist/worker_mcp/src/index.js +3 -3
- package/dist/worker_public/src/config.js +4 -2
- package/dist/worker_public/src/index.js +1193 -4932
- package/dist/worker_public/src/profile.js +1 -1
- package/dist/worker_public/src/refusal.js +2 -2
- package/dist/worker_public/src/requestScope.js +3 -3
- package/docs/spec-api.md +27 -13
- package/package.json +12 -3
- package/profile/prompts.yaml +3 -0
- package/workers/shared/router.ts +23 -16
- package/workers/worker_public/migrations/0014_usage_cache.sql +5 -0
- package/workers/worker_public/openapi.yaml +1169 -0
- package/workers/worker_public/prompts/system.md +1 -0
- package/workers/worker_public/schema.sql +3 -1
- package/workers/worker_public/src/admin.ts +51 -7
- package/workers/worker_public/src/ai.ts +10 -2
- package/workers/worker_public/src/ask.ts +94 -22
- package/workers/worker_public/src/codecs.ts +35 -10
- package/workers/worker_public/src/completion.ts +24 -1
- package/workers/worker_public/src/config.ts +10 -0
- package/workers/worker_public/src/faithfulness.ts +10 -17
- package/workers/worker_public/src/grader.ts +2 -2
- package/workers/worker_public/src/index.ts +106 -58
- package/workers/worker_public/src/lib/router.ts +1 -1
- package/workers/worker_public/src/mcp-proto.ts +71 -0
- package/workers/worker_public/src/mcp.ts +47 -0
- package/workers/worker_public/src/openapi-surface.gen.ts +318 -0
- package/workers/worker_public/src/pipeline.ts +4 -2
- package/workers/worker_public/src/profile.gen.ts +1 -0
- package/workers/worker_public/src/projects.ts +4 -2
- package/workers/worker_public/src/quota.ts +4 -2
- package/workers/worker_public/src/research.ts +55 -3
- package/workers/worker_public/src/tablecontext.ts +13 -2
- package/workers/worker_public/src/verdict-parse.ts +60 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MODELS, datasetsFor, SUGGESTIONS, roleModel } from "./config";
|
|
1
|
+
import { MODELS, datasetsFor, SUGGESTIONS, STARTERS, roleModel } from "./config";
|
|
2
2
|
export { setProfile } from "./profile.ts";
|
|
3
3
|
import { retrieve } from "./pipeline";
|
|
4
4
|
import type { Hit } from "./pipeline";
|
|
@@ -19,18 +19,25 @@ export type { Env };
|
|
|
19
19
|
import { json, err, corsHeaders, withCors, readJson, authenticate, type ApiKey } from "./lib/http";
|
|
20
20
|
|
|
21
21
|
import { handleSearch } from "./search";
|
|
22
|
-
import { handleEnrich, handleSectionUnit, handleCaption, handleVectors, handleJudge, handleCreateKey, handleListKeys, handleRevokeKey } from "./admin";
|
|
22
|
+
import { handleEnrich, handleSectionUnit, handleCaption, handleVectors, handleJudge, handleCreateKey, handleListKeys, handleRevokeKey, handleKeyUsage } from "./admin";
|
|
23
23
|
import { handleResearch } from "./research";
|
|
24
24
|
import { handleAsk } from "./ask";
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
import { handleMcp } from "./mcp";
|
|
26
|
+
|
|
27
|
+
// The HTTP surface is the OpenAPI document (workers/worker_public/openapi.yaml):
|
|
28
|
+
// scripts/gen-openapi-routes.mjs generates openapi-surface.gen.ts from it,
|
|
29
|
+
// and OPENAPI_HANDLERS below binds every operation to its handler. The
|
|
30
|
+
// Record<OpenApiOperationId, …> type makes the binding exhaustive at
|
|
31
|
+
// typecheck, and tests/openapi-surface.test.ts pins the bijection. Adding
|
|
32
|
+
// an endpoint = the yaml entry + one binding + regenerate. Dual-published
|
|
33
|
+
// routes (/api for the browser, /v1 for keyed integrators) bind the SAME
|
|
34
|
+
// handler — the tier split is the handler's, derived from the path.
|
|
35
|
+
// Non-API routes (pages, unit assets, rendered documents) live in
|
|
36
|
+
// INFRA_ROUTES. Mirrored in docs/spec-api.md.
|
|
37
|
+
|
|
38
|
+
import { matchRoute, routeMatchesPath, type RouteContext, type Route, type RouteHandler } from "./lib/router";
|
|
39
|
+
import { OPENAPI_SURFACE } from "./openapi-surface.gen";
|
|
40
|
+
import type { OpenApiOperationId } from "./openapi-surface.gen";
|
|
34
41
|
import { P } from "./profile.ts";
|
|
35
42
|
|
|
36
43
|
async function serveIndexPage(c: RouteContext): Promise<Response> {
|
|
@@ -97,7 +104,7 @@ async function getSharedRoute(c: RouteContext): Promise<Response> {
|
|
|
97
104
|
|
|
98
105
|
async function datasetsRoute(c: RouteContext): Promise<Response> {
|
|
99
106
|
const session = await sessionFrom(c.req, c.env as any);
|
|
100
|
-
return json({ datasets: datasetsFor(session), suggestions: SUGGESTIONS() }, 200, corsHeaders(c.req));
|
|
107
|
+
return json({ datasets: datasetsFor(session), suggestions: SUGGESTIONS(), starters: STARTERS() }, 200, corsHeaders(c.req));
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
async function healthRoute(c: RouteContext): Promise<Response> {
|
|
@@ -120,6 +127,12 @@ async function tierFor(c: RouteContext): Promise<{ tier: "anon" | "key" | "membe
|
|
|
120
127
|
return { tier, key };
|
|
121
128
|
}
|
|
122
129
|
|
|
130
|
+
async function mcpRoute(c: RouteContext): Promise<Response> {
|
|
131
|
+
const t = await tierFor(c);
|
|
132
|
+
if (t instanceof Response) return t;
|
|
133
|
+
return withCors(await handleMcp(c.env, c.ctx, c.req, t.tier, t.key), corsHeaders(c.req));
|
|
134
|
+
}
|
|
135
|
+
|
|
123
136
|
async function askRoute(c: RouteContext): Promise<Response> {
|
|
124
137
|
const t = await tierFor(c);
|
|
125
138
|
if (t instanceof Response) return t;
|
|
@@ -137,14 +150,18 @@ async function adminStatsRoute(c: RouteContext): Promise<Response> {
|
|
|
137
150
|
if (!env.ADMIN_TOKEN) return err(501, "admin_disabled", "ADMIN_TOKEN secret is not configured");
|
|
138
151
|
const auth = req.headers.get("authorization") ?? "";
|
|
139
152
|
if (auth !== `Bearer ${env.ADMIN_TOKEN}`) return err(401, "unauthorized", "Invalid admin token");
|
|
140
|
-
const [byDay, byModel, feedback, convCount] = await Promise.all([
|
|
153
|
+
const [byDay, byModel, feedback, convCount, cacheMix, durations] = await Promise.all([
|
|
141
154
|
env.DB.prepare("SELECT day, tier, COUNT(*) as n, SUM(ok) as ok FROM queries WHERE day >= date('now','-7 days') GROUP BY day, tier ORDER BY day DESC").all(),
|
|
142
155
|
env.DB.prepare("SELECT model, SUM(requests) as requests FROM spend WHERE day >= date('now','-7 days') GROUP BY model ORDER BY requests DESC").all(),
|
|
143
156
|
env.DB.prepare("SELECT rating, COUNT(*) as n FROM feedback GROUP BY rating").all(),
|
|
144
157
|
env.DB.prepare("SELECT COUNT(*) as n FROM conversations").first(),
|
|
158
|
+
env.DB.prepare("SELECT COALESCE(cache, 'miss') AS cache, COUNT(*) AS n FROM queries WHERE day >= date('now','-7 days') AND route = 'ask' GROUP BY cache").all(),
|
|
159
|
+
env.DB.prepare("SELECT duration_ms FROM queries WHERE day >= date('now','-7 days') AND route = 'ask' AND duration_ms IS NOT NULL").all(),
|
|
145
160
|
]);
|
|
161
|
+
const ds = (durations.results as any[]).map((r) => r.duration_ms as number).sort((a, b) => a - b);
|
|
162
|
+
const pct = (q: number) => (ds.length ? ds[Math.min(ds.length - 1, Math.floor(q * ds.length))] : null);
|
|
146
163
|
const totalQueries = (byDay.results as any[]).reduce((a, r) => a + (r.n || 0), 0) || 0;
|
|
147
|
-
const totalOk = (byDay.results as any[]).reduce((a, r) => a + (r.
|
|
164
|
+
const totalOk = (byDay.results as any[]).reduce((a, r) => a + (r.ok || 0), 0) || 0;
|
|
148
165
|
const errorRate = totalQueries > 0 ? (((totalQueries - totalOk) / totalQueries) * 100).toFixed(1) : "0";
|
|
149
166
|
ctx.waitUntil(env.DB.batch([
|
|
150
167
|
env.DB.prepare("DELETE FROM queries WHERE day < date('now','-90 days')"),
|
|
@@ -156,6 +173,8 @@ async function adminStatsRoute(c: RouteContext): Promise<Response> {
|
|
|
156
173
|
queries_by_day: byDay.results,
|
|
157
174
|
spend_by_model: byModel.results,
|
|
158
175
|
feedback: feedback.results,
|
|
176
|
+
cache_mix_7d: cacheMix.results,
|
|
177
|
+
latency_ms: ds.length ? { n: ds.length, p50: pct(0.5), p95: pct(0.95) } : null,
|
|
159
178
|
conversations: (convCount as any)?.n ?? 0,
|
|
160
179
|
error_rate_pct: errorRate,
|
|
161
180
|
index_version: env.INDEX_VERSION,
|
|
@@ -190,7 +209,7 @@ async function absenceRoute(c: RouteContext): Promise<Response> {
|
|
|
190
209
|
enumerated: { model_nodes: nodes.length, smart_model_chunks: chunks?.n ?? 0 },
|
|
191
210
|
matches: matches.slice(0, 20),
|
|
192
211
|
verdict: matches.length === 0 ? "absent" : "present",
|
|
193
|
-
scope: `the model
|
|
212
|
+
scope: `the machine-readable model of ${standard} (all model nodes) — the enumeration is exhaustive over that scope; prose outside the modeled families is not claimed`,
|
|
194
213
|
});
|
|
195
214
|
} catch (e) {
|
|
196
215
|
return err(502, "absence_failed", String(e).slice(0, 200));
|
|
@@ -375,54 +394,78 @@ async function researchRoute(c: RouteContext): Promise<Response> {
|
|
|
375
394
|
return handleResearch(c.env, c.ctx, c.req, session);
|
|
376
395
|
}
|
|
377
396
|
|
|
378
|
-
|
|
397
|
+
// The non-API routes: the HTML pages, the unit-keyed figure assets and
|
|
398
|
+
// the rendered publication documents. Everything else is generated from
|
|
399
|
+
// the OpenAPI document.
|
|
400
|
+
const INFRA_ROUTES: Route[] = [
|
|
379
401
|
{ method: "GET", pattern: "/", handler: serveIndexPage },
|
|
380
402
|
{ method: "GET", pattern: "/api/", handler: serveIndexPage },
|
|
381
403
|
{ method: "GET", pattern: "/index.html", handler: serveIndexPage },
|
|
382
|
-
{ method: "GET", pattern: "/auth/login", handler: (c) => handleLogin(c.env as any, c.req) },
|
|
383
|
-
{ method: "GET", pattern: "/auth/callback", handler: (c) => handleCallback(c.env as any, c.req) },
|
|
384
|
-
{ method: "GET", pattern: "/auth/me", handler: async (c) => withCors(await handleMe(c.env as any, c.req), corsHeaders(c.req)) },
|
|
385
|
-
{ method: "GET", pattern: "/auth/logout", handler: (c) => handleLogout(c.env as any, c.req) },
|
|
386
|
-
{ method: "POST", pattern: "/auth/logout", handler: (c) => handleLogout(c.env as any, c.req) },
|
|
387
|
-
{ method: "*", pattern: "/api/conversations", handler: conversationsRoute },
|
|
388
|
-
{ method: "*", pattern: "/api/memories", handler: memoriesRoute },
|
|
389
|
-
{ method: "*", pattern: "/api/projects", handler: projectsRoute },
|
|
390
|
-
{ method: "*", pattern: "/api/projects/:id/files", handler: projectFilesRoute },
|
|
391
|
-
{ method: "DELETE", pattern: "/api/project-files/:id", handler: projectFilesRoute },
|
|
392
|
-
{ method: "*", pattern: "/api/memories/:id", handler: memoriesRoute },
|
|
393
|
-
{ method: "*", pattern: "/api/conversations/:id", handler: conversationsRoute },
|
|
394
|
-
{ method: "POST", pattern: "/api/conversations/:id/messages", handler: appendMessageRoute },
|
|
395
|
-
{ method: "POST", pattern: "/api/conversations/:id/share", handler: shareRoute },
|
|
396
|
-
{ method: "GET", pattern: "/api/shared/:slug", handler: getSharedRoute },
|
|
397
|
-
{ method: "GET", pattern: "/api/datasets", handler: datasetsRoute },
|
|
398
|
-
{ method: "GET", pattern: "/health", handler: healthRoute },
|
|
399
|
-
{ method: "GET", pattern: "/v1/admin/stats", handler: adminStatsRoute },
|
|
400
|
-
{ method: "POST", pattern: "/api/ask", handler: askRoute },
|
|
401
|
-
{ method: "POST", pattern: "/v1/ask", handler: askRoute },
|
|
402
|
-
{ method: "POST", pattern: "/api/absence", handler: absenceRoute },
|
|
403
|
-
{ method: "POST", pattern: "/v1/absence", handler: absenceRoute },
|
|
404
|
-
{ method: "POST", pattern: "/api/verify", handler: verifyRoute },
|
|
405
|
-
{ method: "POST", pattern: "/v1/verify", handler: verifyRoute },
|
|
406
|
-
{ method: "POST", pattern: "/api/lane", handler: laneRoute },
|
|
407
|
-
{ method: "POST", pattern: "/v1/lane", handler: laneRoute },
|
|
408
|
-
{ method: "POST", pattern: "/api/search", handler: searchRoute },
|
|
409
|
-
{ method: "POST", pattern: "/v1/search", handler: searchRoute },
|
|
410
|
-
{ method: "POST", pattern: "/api/feedback", handler: feedbackRoute },
|
|
411
|
-
{ method: "POST", pattern: "/admin/enrich", handler: (c) => handleEnrich(c.env, c.ctx, c.req) },
|
|
412
|
-
{ method: "POST", pattern: "/v1/admin/enrich", handler: (c) => handleEnrich(c.env, c.ctx, c.req) },
|
|
413
|
-
{ method: "POST", pattern: "/admin/section", handler: (c) => handleSectionUnit(c.env, c.ctx, c.req) },
|
|
414
|
-
{ method: "POST", pattern: "/v1/admin/section", handler: (c) => handleSectionUnit(c.env, c.ctx, c.req) },
|
|
415
|
-
{ method: "POST", pattern: "/admin/vectors", handler: (c) => handleVectors(c.env, c.req) },
|
|
416
|
-
{ method: "POST", pattern: "/admin/caption", handler: (c) => handleCaption(c.env, c.req) },
|
|
417
404
|
{ method: "GET", pattern: "/assets/*", handler: unitAssetRoute },
|
|
418
405
|
{ method: "GET", pattern: "/docs/*", handler: docsRoute },
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
const OPENAPI_HANDLERS: Record<OpenApiOperationId, RouteHandler> = {
|
|
409
|
+
askAnonymous: askRoute,
|
|
410
|
+
askKeyed: askRoute,
|
|
411
|
+
search: searchRoute,
|
|
412
|
+
searchKeyed: searchRoute,
|
|
413
|
+
absence: absenceRoute,
|
|
414
|
+
absenceKeyed: absenceRoute,
|
|
415
|
+
verify: verifyRoute,
|
|
416
|
+
verifyKeyed: verifyRoute,
|
|
417
|
+
research: researchRoute,
|
|
418
|
+
researchKeyed: researchRoute,
|
|
419
|
+
laneQuery: laneRoute,
|
|
420
|
+
laneKeyed: laneRoute,
|
|
421
|
+
mcp: mcpRoute,
|
|
422
|
+
feedback: feedbackRoute,
|
|
423
|
+
datasets: datasetsRoute,
|
|
424
|
+
keyUsage: async (c) => withCors(await handleKeyUsage(c.env, c.req), corsHeaders(c.req)),
|
|
425
|
+
health: healthRoute,
|
|
426
|
+
authLogin: (c) => handleLogin(c.env as any, c.req),
|
|
427
|
+
authCallback: (c) => handleCallback(c.env as any, c.req),
|
|
428
|
+
authMe: async (c) => withCors(await handleMe(c.env as any, c.req), corsHeaders(c.req)),
|
|
429
|
+
authLogout: (c) => handleLogout(c.env as any, c.req),
|
|
430
|
+
authLogoutLink: (c) => handleLogout(c.env as any, c.req),
|
|
431
|
+
listConversations: conversationsRoute,
|
|
432
|
+
createConversation: conversationsRoute,
|
|
433
|
+
getConversation: conversationsRoute,
|
|
434
|
+
renameConversation: conversationsRoute,
|
|
435
|
+
deleteConversation: conversationsRoute,
|
|
436
|
+
appendMessage: appendMessageRoute,
|
|
437
|
+
shareConversation: shareRoute,
|
|
438
|
+
getShared: getSharedRoute,
|
|
439
|
+
listMemories: memoriesRoute,
|
|
440
|
+
createMemory: memoriesRoute,
|
|
441
|
+
deleteMemory: memoriesRoute,
|
|
442
|
+
listProjects: projectsRoute,
|
|
443
|
+
createProject: projectsRoute,
|
|
444
|
+
deleteProject: projectsRoute,
|
|
445
|
+
listProjectFiles: projectFilesRoute,
|
|
446
|
+
attachProjectFile: projectFilesRoute,
|
|
447
|
+
detachProjectFile: projectFilesRoute,
|
|
448
|
+
adminStats: adminStatsRoute,
|
|
449
|
+
adminListKeys: (c) => handleListKeys(c.env, c.req),
|
|
450
|
+
adminCreateKey: (c) => handleCreateKey(c.env, c.req),
|
|
451
|
+
adminRevokeKey: (c) => handleRevokeKey(c.env, c.req, c.params.id),
|
|
452
|
+
adminEnrich: (c) => handleEnrich(c.env, c.ctx, c.req),
|
|
453
|
+
adminEnrichAlias: (c) => handleEnrich(c.env, c.ctx, c.req),
|
|
454
|
+
adminSection: (c) => handleSectionUnit(c.env, c.ctx, c.req),
|
|
455
|
+
adminSectionAlias: (c) => handleSectionUnit(c.env, c.ctx, c.req),
|
|
456
|
+
adminVectors: (c) => handleVectors(c.env, c.req),
|
|
457
|
+
adminCaption: (c) => handleCaption(c.env, c.req),
|
|
458
|
+
adminJudge: (c) => handleJudge(c.env, c.req),
|
|
459
|
+
adminJudgeAlias: (c) => handleJudge(c.env, c.req),
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
export const ROUTES: Route[] = [
|
|
463
|
+
...INFRA_ROUTES,
|
|
464
|
+
...OPENAPI_SURFACE.map((r) => ({
|
|
465
|
+
method: r.method,
|
|
466
|
+
pattern: r.pattern,
|
|
467
|
+
handler: OPENAPI_HANDLERS[r.operationId],
|
|
468
|
+
})),
|
|
426
469
|
];
|
|
427
470
|
|
|
428
471
|
export default {
|
|
@@ -437,6 +480,11 @@ export default {
|
|
|
437
480
|
if (matched) {
|
|
438
481
|
return matched.route.handler({ env, req, ctx, url, path, params: matched.params });
|
|
439
482
|
}
|
|
483
|
+
// the surface declares methods per path; a path that exists under
|
|
484
|
+
// another method is a 405, not a 404
|
|
485
|
+
if (ROUTES.some((r) => routeMatchesPath(r.pattern, path))) {
|
|
486
|
+
return err(405, "method_not_allowed", `The path is served, but not with ${req.method}`);
|
|
487
|
+
}
|
|
440
488
|
return err(404, "not_found", "Unknown route");
|
|
441
489
|
},
|
|
442
490
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// The router lives with the shared cross-worker modules
|
|
2
2
|
// (workers/shared/router.ts) — re-exported for worker_public's imports.
|
|
3
|
-
export { matchRoute } from "../../../shared/router.ts";
|
|
3
|
+
export { matchRoute, routeMatchesPath } from "../../../shared/router.ts";
|
|
4
4
|
export type { RouteContext, Route, RouteHandler } from "../../../shared/router.ts";
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// The MCP protocol surface (JSON-RPC 2.0, streamable HTTP 2025-06-18),
|
|
2
|
+
// dependency-free so plain node can load it for unit tests — the
|
|
3
|
+
// route adapter (mcp.ts) owns the handler wiring.
|
|
4
|
+
export const PROTOCOL_VERSION = "2025-06-18";
|
|
5
|
+
|
|
6
|
+
export interface McpTool {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
inputSchema: { type: "object"; properties: Record<string, unknown>; required: string[] };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const TOOLS: McpTool[] = [
|
|
13
|
+
{
|
|
14
|
+
name: "ask",
|
|
15
|
+
description: "Ask the corpus a question; returns a citation-grounded answer with the passages it rests on.",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
query: { type: "string", description: "The question (1-8000 chars)" },
|
|
20
|
+
lang: { type: "string", description: "Answer language hint (e.g. en, fr)" },
|
|
21
|
+
},
|
|
22
|
+
required: ["query"],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "retrieve",
|
|
27
|
+
description: "Retrieve the top passages for a query (hybrid dense + metadata steering, reranked).",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
query: { type: "string" },
|
|
32
|
+
k: { type: "number", description: "Hits to return (default 5)" },
|
|
33
|
+
},
|
|
34
|
+
required: ["query"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export type McpResult =
|
|
40
|
+
| { ok: true; result: unknown }
|
|
41
|
+
| { ok: true; accepted: true }
|
|
42
|
+
| { ok: false; code: number; message: string };
|
|
43
|
+
|
|
44
|
+
/** Dispatch one JSON-RPC request; `callTool` adapts a tool call to the
|
|
45
|
+
* real handlers (the route adapter's job). */
|
|
46
|
+
export function dispatch(
|
|
47
|
+
method: string | null,
|
|
48
|
+
params: any,
|
|
49
|
+
callTool: (name: string, args: Record<string, unknown>) => Promise<unknown>,
|
|
50
|
+
): Promise<McpResult> {
|
|
51
|
+
switch (method) {
|
|
52
|
+
case "initialize":
|
|
53
|
+
return Promise.resolve({ ok: true, result: { protocolVersion: PROTOCOL_VERSION, capabilities: { tools: {} }, serverInfo: { name: "rag", version: "1.0.0" } } });
|
|
54
|
+
case "notifications/initialized":
|
|
55
|
+
case "ping":
|
|
56
|
+
return Promise.resolve({ ok: true, accepted: true });
|
|
57
|
+
case "tools/list":
|
|
58
|
+
return Promise.resolve({ ok: true, result: { tools: TOOLS } });
|
|
59
|
+
case "tools/call": {
|
|
60
|
+
const name = typeof params?.name === "string" ? params.name : "";
|
|
61
|
+
if (!TOOLS.some((t) => t.name === name)) {
|
|
62
|
+
return Promise.resolve({ ok: false, code: -32602, message: `unknown tool: ${name}` });
|
|
63
|
+
}
|
|
64
|
+
return callTool(name, params?.arguments ?? {}).then(
|
|
65
|
+
(payload) => ({ ok: true, result: { content: [{ type: "text", text: JSON.stringify(payload) }] } }) as McpResult,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
return Promise.resolve({ ok: false, code: -32601, message: `method not found: ${method}` });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// The public-audience MCP server (streamable HTTP): the same
|
|
2
|
+
// ask/retrieve surfaces as /v1, behind the same API-key tiering, for
|
|
3
|
+
// agent ecosystems. A thin adapter by design — each tool call is an
|
|
4
|
+
// internal Request to the exported route handler, so MCP can never
|
|
5
|
+
// drift from the API contract; the protocol dispatch lives in
|
|
6
|
+
// mcp-proto.ts (dependency-free, unit-tested). The internal-audience
|
|
7
|
+
// server federates both indexes and lives in its own worker, never here.
|
|
8
|
+
import { json, readJson, type ApiKey } from "./lib/http";
|
|
9
|
+
import { P } from "./profile.ts";
|
|
10
|
+
import { dispatch } from "./mcp-proto.ts";
|
|
11
|
+
import type { Env } from "./env.ts";
|
|
12
|
+
import type { Background } from "./ports/runtime.ts";
|
|
13
|
+
|
|
14
|
+
export async function handleMcp(
|
|
15
|
+
env: Env,
|
|
16
|
+
// the port type, not the provider token — the handlers cast at their edge
|
|
17
|
+
ctx: Background,
|
|
18
|
+
req: Request,
|
|
19
|
+
tier: "anon" | "key" | "member",
|
|
20
|
+
key: ApiKey | null,
|
|
21
|
+
): Promise<Response> {
|
|
22
|
+
const body = await readJson(req);
|
|
23
|
+
const method = typeof body?.method === "string" ? body.method : null;
|
|
24
|
+
const id = body?.id ?? null;
|
|
25
|
+
|
|
26
|
+
const out = await dispatch(method, body?.params, async (name, args) => {
|
|
27
|
+
const inner = new Request("https://internal/mcp", {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "content-type": "application/json" },
|
|
30
|
+
// stream:false forces the JSON lane (anon defaults to SSE)
|
|
31
|
+
body: JSON.stringify({ ...args, stream: false }),
|
|
32
|
+
});
|
|
33
|
+
// deferred so plain node can load mcp-proto without the handlers'
|
|
34
|
+
// .md prompt imports, which only the bundler resolves
|
|
35
|
+
const res = name === "ask"
|
|
36
|
+
? await (await import("./ask")).handleAsk(env, ctx as any, inner, tier, key)
|
|
37
|
+
: await (await import("./search")).handleSearch(env, ctx as any, inner, tier, key);
|
|
38
|
+
return res.json().catch(() => ({ error: { message: "tool transport failed", status: res.status } }));
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (out.ok && "accepted" in out) return new Response(null, { status: 202 });
|
|
42
|
+
if (out.ok) {
|
|
43
|
+
if ((out.result as any)?.serverInfo) (out.result as any).serverInfo.name = `${P().publisher.id}-rag`;
|
|
44
|
+
return json({ jsonrpc: "2.0", id, result: out.result });
|
|
45
|
+
}
|
|
46
|
+
return json({ jsonrpc: "2.0", id, error: { code: out.code, message: out.message } });
|
|
47
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
// GENERATED from workers/worker_public/openapi.yaml — do not edit.
|
|
2
|
+
// Regenerate: node scripts/gen-openapi-routes.mjs
|
|
3
|
+
export interface OpenApiRoute {
|
|
4
|
+
method: string
|
|
5
|
+
pattern: string
|
|
6
|
+
operationId: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type OpenApiOperationId =
|
|
10
|
+
| "askAnonymous"
|
|
11
|
+
| "askKeyed"
|
|
12
|
+
| "search"
|
|
13
|
+
| "searchKeyed"
|
|
14
|
+
| "absence"
|
|
15
|
+
| "absenceKeyed"
|
|
16
|
+
| "verify"
|
|
17
|
+
| "verifyKeyed"
|
|
18
|
+
| "research"
|
|
19
|
+
| "researchKeyed"
|
|
20
|
+
| "mcp"
|
|
21
|
+
| "datasets"
|
|
22
|
+
| "keyUsage"
|
|
23
|
+
| "health"
|
|
24
|
+
| "listConversations"
|
|
25
|
+
| "createConversation"
|
|
26
|
+
| "getConversation"
|
|
27
|
+
| "renameConversation"
|
|
28
|
+
| "deleteConversation"
|
|
29
|
+
| "appendMessage"
|
|
30
|
+
| "shareConversation"
|
|
31
|
+
| "getShared"
|
|
32
|
+
| "listMemories"
|
|
33
|
+
| "createMemory"
|
|
34
|
+
| "deleteMemory"
|
|
35
|
+
| "listProjects"
|
|
36
|
+
| "createProject"
|
|
37
|
+
| "deleteProject"
|
|
38
|
+
| "listProjectFiles"
|
|
39
|
+
| "attachProjectFile"
|
|
40
|
+
| "detachProjectFile"
|
|
41
|
+
| "laneQuery"
|
|
42
|
+
| "laneKeyed"
|
|
43
|
+
| "feedback"
|
|
44
|
+
| "adminEnrich"
|
|
45
|
+
| "adminEnrichAlias"
|
|
46
|
+
| "adminSection"
|
|
47
|
+
| "adminSectionAlias"
|
|
48
|
+
| "adminVectors"
|
|
49
|
+
| "adminCaption"
|
|
50
|
+
| "adminJudge"
|
|
51
|
+
| "adminJudgeAlias"
|
|
52
|
+
| "adminRevokeKey"
|
|
53
|
+
| "authMe"
|
|
54
|
+
| "authLogin"
|
|
55
|
+
| "authCallback"
|
|
56
|
+
| "authLogout"
|
|
57
|
+
| "authLogoutLink"
|
|
58
|
+
| "adminStats"
|
|
59
|
+
| "adminListKeys"
|
|
60
|
+
| "adminCreateKey"
|
|
61
|
+
|
|
62
|
+
export const OPENAPI_SURFACE: readonly (Omit<OpenApiRoute, "operationId"> & { operationId: OpenApiOperationId })[] = [
|
|
63
|
+
{
|
|
64
|
+
"method": "POST",
|
|
65
|
+
"pattern": "/api/ask",
|
|
66
|
+
"operationId": "askAnonymous"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"method": "POST",
|
|
70
|
+
"pattern": "/v1/ask",
|
|
71
|
+
"operationId": "askKeyed"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"method": "POST",
|
|
75
|
+
"pattern": "/api/search",
|
|
76
|
+
"operationId": "search"
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"method": "POST",
|
|
80
|
+
"pattern": "/v1/search",
|
|
81
|
+
"operationId": "searchKeyed"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"method": "POST",
|
|
85
|
+
"pattern": "/api/absence",
|
|
86
|
+
"operationId": "absence"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"method": "POST",
|
|
90
|
+
"pattern": "/v1/absence",
|
|
91
|
+
"operationId": "absenceKeyed"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"method": "POST",
|
|
95
|
+
"pattern": "/api/verify",
|
|
96
|
+
"operationId": "verify"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"method": "POST",
|
|
100
|
+
"pattern": "/v1/verify",
|
|
101
|
+
"operationId": "verifyKeyed"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"method": "POST",
|
|
105
|
+
"pattern": "/api/research",
|
|
106
|
+
"operationId": "research"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"method": "POST",
|
|
110
|
+
"pattern": "/v1/research",
|
|
111
|
+
"operationId": "researchKeyed"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"method": "POST",
|
|
115
|
+
"pattern": "/mcp",
|
|
116
|
+
"operationId": "mcp"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"method": "GET",
|
|
120
|
+
"pattern": "/api/datasets",
|
|
121
|
+
"operationId": "datasets"
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"method": "GET",
|
|
125
|
+
"pattern": "/v1/usage",
|
|
126
|
+
"operationId": "keyUsage"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"method": "GET",
|
|
130
|
+
"pattern": "/health",
|
|
131
|
+
"operationId": "health"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"method": "GET",
|
|
135
|
+
"pattern": "/api/conversations",
|
|
136
|
+
"operationId": "listConversations"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"method": "POST",
|
|
140
|
+
"pattern": "/api/conversations",
|
|
141
|
+
"operationId": "createConversation"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"method": "GET",
|
|
145
|
+
"pattern": "/api/conversations/:id",
|
|
146
|
+
"operationId": "getConversation"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"method": "PATCH",
|
|
150
|
+
"pattern": "/api/conversations/:id",
|
|
151
|
+
"operationId": "renameConversation"
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
"method": "DELETE",
|
|
155
|
+
"pattern": "/api/conversations/:id",
|
|
156
|
+
"operationId": "deleteConversation"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"method": "POST",
|
|
160
|
+
"pattern": "/api/conversations/:id/messages",
|
|
161
|
+
"operationId": "appendMessage"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
"method": "POST",
|
|
165
|
+
"pattern": "/api/conversations/:id/share",
|
|
166
|
+
"operationId": "shareConversation"
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"method": "GET",
|
|
170
|
+
"pattern": "/api/shared/:slug",
|
|
171
|
+
"operationId": "getShared"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"method": "GET",
|
|
175
|
+
"pattern": "/api/memories",
|
|
176
|
+
"operationId": "listMemories"
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"method": "POST",
|
|
180
|
+
"pattern": "/api/memories",
|
|
181
|
+
"operationId": "createMemory"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"method": "DELETE",
|
|
185
|
+
"pattern": "/api/memories/:id",
|
|
186
|
+
"operationId": "deleteMemory"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"method": "GET",
|
|
190
|
+
"pattern": "/api/projects",
|
|
191
|
+
"operationId": "listProjects"
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
"method": "POST",
|
|
195
|
+
"pattern": "/api/projects",
|
|
196
|
+
"operationId": "createProject"
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
"method": "DELETE",
|
|
200
|
+
"pattern": "/api/projects/:id",
|
|
201
|
+
"operationId": "deleteProject"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"method": "GET",
|
|
205
|
+
"pattern": "/api/projects/:id/files",
|
|
206
|
+
"operationId": "listProjectFiles"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"method": "POST",
|
|
210
|
+
"pattern": "/api/projects/:id/files",
|
|
211
|
+
"operationId": "attachProjectFile"
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
"method": "DELETE",
|
|
215
|
+
"pattern": "/api/project-files/:id",
|
|
216
|
+
"operationId": "detachProjectFile"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"method": "POST",
|
|
220
|
+
"pattern": "/api/lane",
|
|
221
|
+
"operationId": "laneQuery"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"method": "POST",
|
|
225
|
+
"pattern": "/v1/lane",
|
|
226
|
+
"operationId": "laneKeyed"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"method": "POST",
|
|
230
|
+
"pattern": "/api/feedback",
|
|
231
|
+
"operationId": "feedback"
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"method": "POST",
|
|
235
|
+
"pattern": "/v1/admin/enrich",
|
|
236
|
+
"operationId": "adminEnrich"
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"method": "POST",
|
|
240
|
+
"pattern": "/admin/enrich",
|
|
241
|
+
"operationId": "adminEnrichAlias"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"method": "POST",
|
|
245
|
+
"pattern": "/v1/admin/section",
|
|
246
|
+
"operationId": "adminSection"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"method": "POST",
|
|
250
|
+
"pattern": "/admin/section",
|
|
251
|
+
"operationId": "adminSectionAlias"
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"method": "POST",
|
|
255
|
+
"pattern": "/admin/vectors",
|
|
256
|
+
"operationId": "adminVectors"
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"method": "POST",
|
|
260
|
+
"pattern": "/admin/caption",
|
|
261
|
+
"operationId": "adminCaption"
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"method": "POST",
|
|
265
|
+
"pattern": "/v1/admin/judge",
|
|
266
|
+
"operationId": "adminJudge"
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"method": "POST",
|
|
270
|
+
"pattern": "/admin/judge",
|
|
271
|
+
"operationId": "adminJudgeAlias"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
"method": "DELETE",
|
|
275
|
+
"pattern": "/v1/admin/keys/:id",
|
|
276
|
+
"operationId": "adminRevokeKey"
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"method": "GET",
|
|
280
|
+
"pattern": "/auth/me",
|
|
281
|
+
"operationId": "authMe"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"method": "GET",
|
|
285
|
+
"pattern": "/auth/login",
|
|
286
|
+
"operationId": "authLogin"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"method": "GET",
|
|
290
|
+
"pattern": "/auth/callback",
|
|
291
|
+
"operationId": "authCallback"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"method": "POST",
|
|
295
|
+
"pattern": "/auth/logout",
|
|
296
|
+
"operationId": "authLogout"
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"method": "GET",
|
|
300
|
+
"pattern": "/auth/logout",
|
|
301
|
+
"operationId": "authLogoutLink"
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"method": "GET",
|
|
305
|
+
"pattern": "/v1/admin/stats",
|
|
306
|
+
"operationId": "adminStats"
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"method": "GET",
|
|
310
|
+
"pattern": "/v1/admin/keys",
|
|
311
|
+
"operationId": "adminListKeys"
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"method": "POST",
|
|
315
|
+
"pattern": "/v1/admin/keys",
|
|
316
|
+
"operationId": "adminCreateKey"
|
|
317
|
+
}
|
|
318
|
+
] as const
|