@firstlovecenter/ai-chat 0.1.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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/drizzle/index.cjs +199 -0
- package/dist/drizzle/index.cjs.map +1 -0
- package/dist/drizzle/index.d.cts +361 -0
- package/dist/drizzle/index.d.ts +361 -0
- package/dist/drizzle/index.js +194 -0
- package/dist/drizzle/index.js.map +1 -0
- package/dist/prisma/index.cjs +163 -0
- package/dist/prisma/index.cjs.map +1 -0
- package/dist/prisma/index.d.cts +163 -0
- package/dist/prisma/index.d.ts +163 -0
- package/dist/prisma/index.js +160 -0
- package/dist/prisma/index.js.map +1 -0
- package/dist/server/index.cjs +1465 -0
- package/dist/server/index.cjs.map +1 -0
- package/dist/server/index.d.cts +487 -0
- package/dist/server/index.d.ts +487 -0
- package/dist/server/index.js +1450 -0
- package/dist/server/index.js.map +1 -0
- package/dist/types-DNwFvL-C.d.cts +268 -0
- package/dist/types-DNwFvL-C.d.ts +268 -0
- package/dist/ui/index.cjs +1388 -0
- package/dist/ui/index.cjs.map +1 -0
- package/dist/ui/index.d.cts +89 -0
- package/dist/ui/index.d.ts +89 -0
- package/dist/ui/index.js +1365 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +112 -0
- package/prisma/chat-models.prisma +61 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
import { S as SystemBlock, T as ToolSchema, a as ToolContext, b as ToolDefinition, P as PresentPayload, c as PersistencePort, A as AuthPort, d as ScopePort, e as ToolsPort, V as VertexPort, L as LoggerPort } from '../types-DNwFvL-C.js';
|
|
2
|
+
export { f as AiSettings, g as AiSettingsPatch, h as AppendMessageInput, i as AuthFail, j as AuthOk, k as AuthResult, B as Block, C as ChartSpec, l as ChatMessage, m as ChatMessageRole, n as ChatSession, o as CreateSessionInput, p as ListSessionsOpts, q as TERMINAL_TOOL_NAME, r as ToolResult, s as err, t as ok } from '../types-DNwFvL-C.js';
|
|
3
|
+
import { GoogleAuth } from 'google-auth-library';
|
|
4
|
+
export { GoogleAuth } from 'google-auth-library';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Provider-agnostic tool-calling abstraction.
|
|
8
|
+
*
|
|
9
|
+
* The agent loop is written against `ToolProvider`, not against any one
|
|
10
|
+
* vendor SDK. Two adapters live alongside this file — `claude.ts`
|
|
11
|
+
* (Anthropic Messages on Vertex) and `gemini.ts` (Google Gemini on
|
|
12
|
+
* Vertex) — and they translate between this normalized shape and each
|
|
13
|
+
* vendor's wire format.
|
|
14
|
+
*
|
|
15
|
+
* Design notes:
|
|
16
|
+
*
|
|
17
|
+
* - `NormalizedMessage` carries the full conversation turn-by-turn:
|
|
18
|
+
* user prompts, assistant replies (text + tool calls), and the
|
|
19
|
+
* matching tool results. Storing both `id` and `name` on every tool
|
|
20
|
+
* call lets us survive Gemini's name-keyed function responses (no
|
|
21
|
+
* IDs) and Anthropic's id-keyed `tool_result` blocks with one shape.
|
|
22
|
+
*
|
|
23
|
+
* - `SystemBlock.cached` is advisory: Anthropic uses it to mark
|
|
24
|
+
* `cache_control: ephemeral`, Gemini ignores it (Vertex Gemini
|
|
25
|
+
* auto-caches stable prefixes). Both still see the same text, in
|
|
26
|
+
* the same order — semantics are preserved across the swap.
|
|
27
|
+
*
|
|
28
|
+
* - The schema we accept here is the JSON Schema declared on each
|
|
29
|
+
* tool. The Gemini-side adapter calls `toGeminiSchema()` to
|
|
30
|
+
* translate `oneOf` → `anyOf`, `const` → enum, etc.
|
|
31
|
+
*
|
|
32
|
+
* - `SystemBlock` and `ToolSchema` are owned by `../tools/types` so the
|
|
33
|
+
* package has a single source of truth. We re-export them from this
|
|
34
|
+
* module so callers can keep `import { SystemBlock } from './types'`.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
type ToolProviderId = 'claude' | 'gemini';
|
|
38
|
+
/** A single tool invocation requested by the model. */
|
|
39
|
+
type NormalizedToolCall = {
|
|
40
|
+
/** Stable per-turn id we generate or echo from the provider. */
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
input: Record<string, unknown>;
|
|
44
|
+
};
|
|
45
|
+
/** A single tool result fed back to the model on the next turn. */
|
|
46
|
+
type NormalizedToolResult = {
|
|
47
|
+
/** Matches the corresponding NormalizedToolCall.id from the prior turn. */
|
|
48
|
+
toolCallId: string;
|
|
49
|
+
toolName: string;
|
|
50
|
+
isError: boolean;
|
|
51
|
+
/** JSON-stringified result body. */
|
|
52
|
+
content: string;
|
|
53
|
+
};
|
|
54
|
+
type NormalizedMessage = {
|
|
55
|
+
role: 'user';
|
|
56
|
+
text: string;
|
|
57
|
+
} | {
|
|
58
|
+
role: 'assistant';
|
|
59
|
+
/** Free text the model emitted (zero-or-more text blocks joined as-is). */
|
|
60
|
+
text: string;
|
|
61
|
+
toolCalls: NormalizedToolCall[];
|
|
62
|
+
/**
|
|
63
|
+
* Vendor-opaque blob the producing adapter wants echoed back on
|
|
64
|
+
* the next turn. Used by the Gemini adapter to preserve the raw
|
|
65
|
+
* response parts (incl. `thoughtSignature` + `thought` summaries)
|
|
66
|
+
* which Gemini 2.5+/3.x thinking mode REQUIRES round-tripped, or
|
|
67
|
+
* the next request fails with `INVALID_ARGUMENT: missing
|
|
68
|
+
* thought_signature`. Other adapters can ignore.
|
|
69
|
+
*/
|
|
70
|
+
providerData?: unknown;
|
|
71
|
+
} | {
|
|
72
|
+
role: 'tool';
|
|
73
|
+
results: NormalizedToolResult[];
|
|
74
|
+
};
|
|
75
|
+
type AgentTurnInput = {
|
|
76
|
+
system: SystemBlock[];
|
|
77
|
+
tools: ToolSchema[];
|
|
78
|
+
messages: NormalizedMessage[];
|
|
79
|
+
maxOutputTokens: number;
|
|
80
|
+
};
|
|
81
|
+
type AgentTurn = {
|
|
82
|
+
/** Concatenated text the model emitted in this turn (may be empty). */
|
|
83
|
+
text: string;
|
|
84
|
+
/** Tool calls the model wants run. Empty means the model ended its turn. */
|
|
85
|
+
toolCalls: NormalizedToolCall[];
|
|
86
|
+
/** Vendor stop reason, normalized loosely. */
|
|
87
|
+
stopReason: 'tool_use' | 'end_turn' | 'max_tokens' | 'other';
|
|
88
|
+
/** Opaque vendor data the agent loop should attach to the assistant message and replay on the next turn. See `NormalizedMessage.providerData`. */
|
|
89
|
+
providerData?: unknown;
|
|
90
|
+
};
|
|
91
|
+
interface ToolProvider {
|
|
92
|
+
readonly id: ToolProviderId;
|
|
93
|
+
/** Run one model turn. Adapters MUST NOT throw on tool errors — those are passed back as `tool` results on the next call. */
|
|
94
|
+
runTurn(input: AgentTurnInput): Promise<AgentTurn>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Agent tool loop.
|
|
99
|
+
*
|
|
100
|
+
* Drives an injected `ToolProvider` (Claude on Vertex, Gemini on Vertex,
|
|
101
|
+
* or any future adapter that satisfies the contract) with a host-supplied
|
|
102
|
+
* tool catalogue until the model invokes the terminal `present` tool (or
|
|
103
|
+
* hits a hard stop without calling it). Returns the structured
|
|
104
|
+
* `PresentPayload`; the prose narrator pass happens in the route handler.
|
|
105
|
+
*
|
|
106
|
+
* Caching: the system prompt blocks are passed through as the host built
|
|
107
|
+
* them. Blocks marked `cached: true` become Anthropic ephemeral cache
|
|
108
|
+
* markers; Gemini ignores the flag and relies on Vertex's automatic prefix
|
|
109
|
+
* caching. Either way a follow-up question within the cache TTL only pays
|
|
110
|
+
* for the question + tool turns.
|
|
111
|
+
*
|
|
112
|
+
* This package's loop is project-agnostic: tools, prompts, and the
|
|
113
|
+
* provider instance are all injected via `AgentInput`. The host owns the
|
|
114
|
+
* registry (`ToolsPort.tools`), the system blocks (`ToolsPort.buildSystemBlocks`),
|
|
115
|
+
* and the constructed provider (resolved through `toolProviders[id].createProvider`).
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
declare const DEFAULT_MAX_TOOL_TURNS = 12;
|
|
119
|
+
declare const DEFAULT_MAX_OUTPUT_TOKENS = 4096;
|
|
120
|
+
type AgentResult = {
|
|
121
|
+
ok: true;
|
|
122
|
+
structured: PresentPayload;
|
|
123
|
+
toolCallCount: number;
|
|
124
|
+
transcript: TranscriptEntry[];
|
|
125
|
+
} | {
|
|
126
|
+
ok: false;
|
|
127
|
+
error: {
|
|
128
|
+
code: string;
|
|
129
|
+
message: string;
|
|
130
|
+
};
|
|
131
|
+
transcript: TranscriptEntry[];
|
|
132
|
+
};
|
|
133
|
+
type TranscriptEntry = {
|
|
134
|
+
kind: 'user';
|
|
135
|
+
text: string;
|
|
136
|
+
} | {
|
|
137
|
+
kind: 'assistant_text';
|
|
138
|
+
text: string;
|
|
139
|
+
} | {
|
|
140
|
+
kind: 'tool_use';
|
|
141
|
+
name: string;
|
|
142
|
+
input: unknown;
|
|
143
|
+
} | {
|
|
144
|
+
kind: 'tool_result';
|
|
145
|
+
name: string;
|
|
146
|
+
result: unknown;
|
|
147
|
+
};
|
|
148
|
+
type AgentInput<S = unknown> = {
|
|
149
|
+
question: string;
|
|
150
|
+
ctx: ToolContext<S>;
|
|
151
|
+
/** Injected tool registry (host-supplied via ToolsPort). MUST include the terminal `present` tool. */
|
|
152
|
+
tools: Record<string, ToolDefinition<unknown, S>>;
|
|
153
|
+
/** Pre-built system blocks (host-supplied via ToolsPort.buildSystemBlocks). */
|
|
154
|
+
systemBlocks: SystemBlock[];
|
|
155
|
+
/** Constructed ToolProvider — caller resolves the right one via toolProviders[id].createProvider({...}). */
|
|
156
|
+
provider: ToolProvider;
|
|
157
|
+
/** Optional caps. Default both. */
|
|
158
|
+
maxToolTurns?: number;
|
|
159
|
+
maxOutputTokens?: number;
|
|
160
|
+
};
|
|
161
|
+
declare function runAgent<S = unknown>(input: AgentInput<S>): Promise<AgentResult>;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Tool-calling provider registry.
|
|
165
|
+
*
|
|
166
|
+
* The agent loop and the routes layer drive providers through this
|
|
167
|
+
* factory; vendor specifics stay inside `claude.ts` / `gemini.ts`.
|
|
168
|
+
* Adding a third provider would slot in alongside without further
|
|
169
|
+
* changes to the agent loop.
|
|
170
|
+
*
|
|
171
|
+
* The host injects credentials (a `GoogleAuth` instance), the GCP
|
|
172
|
+
* project id, the default region, and the pinned model ids via
|
|
173
|
+
* `VertexPort`. A per-request region override flows in as
|
|
174
|
+
* `ProviderInitOpts.location` so admin-managed `aiSettings.gcpLocation`
|
|
175
|
+
* can flip Vertex regions without rebuilding the registry.
|
|
176
|
+
*/
|
|
177
|
+
|
|
178
|
+
type ProviderInitOpts = {
|
|
179
|
+
/** Pre-built GoogleAuth instance (host-supplied). */
|
|
180
|
+
auth: GoogleAuth;
|
|
181
|
+
/** GCP project id. */
|
|
182
|
+
projectId: string;
|
|
183
|
+
/** Default Vertex region used when `location` is omitted. */
|
|
184
|
+
defaultLocation: string;
|
|
185
|
+
/** Vertex model ids pinned by the host. */
|
|
186
|
+
modelIds: {
|
|
187
|
+
claude: string;
|
|
188
|
+
gemini: string;
|
|
189
|
+
};
|
|
190
|
+
/** Per-request override for `aiSettings.gcpLocation`. Falls back to defaultLocation. */
|
|
191
|
+
location?: string;
|
|
192
|
+
};
|
|
193
|
+
type ToolProviderDef = {
|
|
194
|
+
id: ToolProviderId;
|
|
195
|
+
label: string;
|
|
196
|
+
description: string;
|
|
197
|
+
createProvider(opts: ProviderInitOpts): ToolProvider;
|
|
198
|
+
};
|
|
199
|
+
declare const toolProviders: ToolProviderDef[];
|
|
200
|
+
declare function getToolProvider(id: string): ToolProviderDef | undefined;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Narrators — the prose pass that turns a structured answer into the
|
|
204
|
+
* paragraph_brief block's flowing text. The agent's tool loop decides
|
|
205
|
+
* WHAT to say (key_facts); the narrator decides HOW to say it.
|
|
206
|
+
*
|
|
207
|
+
* Three implementations:
|
|
208
|
+
* - streamClaudeNarration — Anthropic Messages on GCP Vertex
|
|
209
|
+
* - streamGrokNarration — xAI Grok via Vertex's OpenAI-compatible endpoint
|
|
210
|
+
* - streamGeminiNarration — Google Gemini on GCP Vertex
|
|
211
|
+
*
|
|
212
|
+
* All three expose the same shape: an async generator yielding raw text
|
|
213
|
+
* deltas. The route layer is responsible for any SSE framing on top — the
|
|
214
|
+
* package keeps the streaming primitive provider-agnostic.
|
|
215
|
+
*
|
|
216
|
+
* Lifted from the host's `src/ai/narrators/`. The host's class-based
|
|
217
|
+
* `NarrativeProvider` interface and `createNarrativeProvider` factory are
|
|
218
|
+
* replaced with plain streaming functions plus a `getNarrator(id)` lookup.
|
|
219
|
+
* Every credential field comes in as an explicit argument; no `@/...`
|
|
220
|
+
* imports remain.
|
|
221
|
+
*/
|
|
222
|
+
|
|
223
|
+
type NarratorId = 'claude' | 'gemini' | 'grok';
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Common lifecycle hooks shared across every route factory. Hooks return
|
|
227
|
+
* `Response | null`: a non-null Response short-circuits the request (the
|
|
228
|
+
* factory returns it untouched), `null` continues the normal flow.
|
|
229
|
+
*/
|
|
230
|
+
type RouteHooks$1<S> = {
|
|
231
|
+
/** Runs before auth. Return a Response to short-circuit (e.g. 503 during shutdown). */
|
|
232
|
+
onRequest?(req: Request): Promise<Response | null>;
|
|
233
|
+
/** Runs after successful auth. Return a Response to short-circuit (e.g. 429 rate-limited). */
|
|
234
|
+
onAuthenticated?(args: {
|
|
235
|
+
req: Request;
|
|
236
|
+
scope: S;
|
|
237
|
+
userId: number;
|
|
238
|
+
}): Promise<Response | null>;
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Streaming-route hooks. Adds three lifecycle points specific to the SSE
|
|
242
|
+
* `agent-custom` path so consumers can plumb in per-request resources
|
|
243
|
+
* (e.g. SQL view creation/cleanup keyed off a fresh sessionId).
|
|
244
|
+
*/
|
|
245
|
+
type AgentCustomHooks$1<S> = RouteHooks$1<S> & {
|
|
246
|
+
/**
|
|
247
|
+
* Generate the per-request session id used in ToolContext (and any
|
|
248
|
+
* project-specific resources keyed off it, e.g. SQL view names).
|
|
249
|
+
* Defaults to a random URL-safe id (16 hex chars from a UUID).
|
|
250
|
+
*/
|
|
251
|
+
generateSessionId?(args: {
|
|
252
|
+
scope: S;
|
|
253
|
+
userId: number;
|
|
254
|
+
chatSessionId: number | null;
|
|
255
|
+
}): string | Promise<string>;
|
|
256
|
+
/**
|
|
257
|
+
* Runs once after the session id is resolved, before the agent loop.
|
|
258
|
+
* Throw to abort the request (the route catches and surfaces the error
|
|
259
|
+
* via the SSE error frame + persistence).
|
|
260
|
+
*/
|
|
261
|
+
onSessionStart?(args: {
|
|
262
|
+
scope: S;
|
|
263
|
+
sessionId: string;
|
|
264
|
+
userId: number;
|
|
265
|
+
}): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Always runs in `finally`, regardless of how the stream ended.
|
|
268
|
+
* The route never throws out of this hook — its errors are logged via
|
|
269
|
+
* ctx.logger but don't surface to the client.
|
|
270
|
+
*/
|
|
271
|
+
onSessionEnd?(args: {
|
|
272
|
+
scope: S;
|
|
273
|
+
sessionId: string;
|
|
274
|
+
userId: number;
|
|
275
|
+
cause: 'complete' | 'error' | 'abort';
|
|
276
|
+
}): Promise<void>;
|
|
277
|
+
};
|
|
278
|
+
type AgentCustomRouteCtx<S> = {
|
|
279
|
+
persistence: PersistencePort;
|
|
280
|
+
auth: AuthPort<S>;
|
|
281
|
+
scope: ScopePort<S>;
|
|
282
|
+
tools: ToolsPort;
|
|
283
|
+
vertex: VertexPort;
|
|
284
|
+
logger?: LoggerPort;
|
|
285
|
+
/**
|
|
286
|
+
* Resolve which narrator to use for prose generation. Default:
|
|
287
|
+
* `() => aiSettings.toolProvider` (which is only ever `claude` or
|
|
288
|
+
* `gemini` from the registry). Hosts that surface a per-user
|
|
289
|
+
* `narrative_provider` (e.g. allowing `grok`) wire their existing
|
|
290
|
+
* lookup here.
|
|
291
|
+
*/
|
|
292
|
+
resolveNarratorId?: (scope: S) => Promise<NarratorId>;
|
|
293
|
+
/**
|
|
294
|
+
* Optional lifecycle hooks. See `AgentCustomHooks` for the available
|
|
295
|
+
* extension points (shutdown gating, rate limiting, per-request
|
|
296
|
+
* resource setup/teardown).
|
|
297
|
+
*/
|
|
298
|
+
hooks?: AgentCustomHooks$1<S>;
|
|
299
|
+
};
|
|
300
|
+
declare function createAgentCustomRoutes<S>(ctx: AgentCustomRouteCtx<S>): {
|
|
301
|
+
/** Next.js-compatible POST handler. */
|
|
302
|
+
POST: (req: Request) => Promise<Response>;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* `chat-sessions` route factory — host-agnostic CRUD for chat sessions.
|
|
307
|
+
*
|
|
308
|
+
* Mounts at `/api/chat/sessions` (list+create) and `/api/chat/sessions/[id]`
|
|
309
|
+
* (get one with messages, rename, delete). Auth and persistence cross the
|
|
310
|
+
* boundary as ports; the package never touches a DB or session adapter.
|
|
311
|
+
*
|
|
312
|
+
* Wire format mirrors the host's pre-extraction Next route handlers so the
|
|
313
|
+
* existing UI keeps working unchanged when the host swaps to these factories.
|
|
314
|
+
*/
|
|
315
|
+
|
|
316
|
+
type ChatSessionsRouteCtx<S> = {
|
|
317
|
+
persistence: PersistencePort;
|
|
318
|
+
auth: AuthPort<S>;
|
|
319
|
+
logger?: LoggerPort;
|
|
320
|
+
/**
|
|
321
|
+
* Optional pre-auth / post-auth hooks (e.g. shutdown gate, rate
|
|
322
|
+
* limiting). The streaming-specific hooks on `AgentCustomHooks` are
|
|
323
|
+
* ignored here — only `onRequest` and `onAuthenticated` apply.
|
|
324
|
+
*/
|
|
325
|
+
hooks?: RouteHooks$1<S>;
|
|
326
|
+
};
|
|
327
|
+
declare function createChatSessionsRoutes<S>(ctx: ChatSessionsRouteCtx<S>): {
|
|
328
|
+
list: {
|
|
329
|
+
/**
|
|
330
|
+
* `GET /api/chat/sessions` — caller's recent sessions, newest first,
|
|
331
|
+
* capped at 100. Response: `{ sessions: [{ id, title, createdAt, updatedAt }] }`.
|
|
332
|
+
*/
|
|
333
|
+
GET: (req: Request) => Promise<Response>;
|
|
334
|
+
/**
|
|
335
|
+
* `POST /api/chat/sessions` — body `{ title?: string }`. Trims and caps
|
|
336
|
+
* title at 200 chars; defaults to "New chat" when blank.
|
|
337
|
+
* Response: `{ session: { id, title, createdAt, updatedAt } }`.
|
|
338
|
+
*/
|
|
339
|
+
POST: (req: Request) => Promise<Response>;
|
|
340
|
+
};
|
|
341
|
+
detail: {
|
|
342
|
+
/**
|
|
343
|
+
* `GET /api/chat/sessions/[id]` — session metadata + ordered messages.
|
|
344
|
+
* 404 when the id doesn't exist or doesn't belong to the caller (we never
|
|
345
|
+
* differentiate the two, to avoid leaking the id space).
|
|
346
|
+
* Response: `{ session: { id, title, createdAt, updatedAt },
|
|
347
|
+
* messages: [{ id, role, question, blocks, prose, errorJson, createdAt }] }`.
|
|
348
|
+
*/
|
|
349
|
+
GET: (req: Request, params: {
|
|
350
|
+
id: string;
|
|
351
|
+
}) => Promise<Response>;
|
|
352
|
+
/**
|
|
353
|
+
* `PATCH /api/chat/sessions/[id]` — rename. Body `{ title: string }`,
|
|
354
|
+
* trimmed and capped at 200 chars. Response: `{ ok: true }`.
|
|
355
|
+
*/
|
|
356
|
+
PATCH: (req: Request, params: {
|
|
357
|
+
id: string;
|
|
358
|
+
}) => Promise<Response>;
|
|
359
|
+
/**
|
|
360
|
+
* `DELETE /api/chat/sessions/[id]` — drop session and its messages.
|
|
361
|
+
* Response: `{ ok: true }`.
|
|
362
|
+
*/
|
|
363
|
+
DELETE: (req: Request, params: {
|
|
364
|
+
id: string;
|
|
365
|
+
}) => Promise<Response>;
|
|
366
|
+
};
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* `/api/admin/ai-settings` route factory — global AI configuration (super_admin only).
|
|
371
|
+
*
|
|
372
|
+
* Three patchable fields on the singleton settings row:
|
|
373
|
+
* - `tool_provider` — vendor that drives the agent tool loop. Validated
|
|
374
|
+
* against the registered `toolProviders` registry passed in via ctx.
|
|
375
|
+
* - `gcp_location` — the Vertex region every provider call hits. Stays
|
|
376
|
+
* a fixed list ('us-east5', 'global') because those are the only
|
|
377
|
+
* regions Claude/Gemini are published in on Vertex.
|
|
378
|
+
* - `chat_interface` — which chat UI module renders globally. Validated
|
|
379
|
+
* against the `chatInterfaces` registry passed in via ctx (the actual
|
|
380
|
+
* registry lives in `@firstlovecenter/ai-chat/ui` so the host wires it through;
|
|
381
|
+
* the route stays free of UI imports).
|
|
382
|
+
*
|
|
383
|
+
* Wire format is snake_case to preserve byte-for-byte parity with the
|
|
384
|
+
* host route the package replaces — existing host UIs keep working
|
|
385
|
+
* unmodified.
|
|
386
|
+
*/
|
|
387
|
+
|
|
388
|
+
type AdminSettingsRouteCtx<S> = {
|
|
389
|
+
persistence: PersistencePort;
|
|
390
|
+
auth: AuthPort<S>;
|
|
391
|
+
/** Registered tool providers (default: built-in toolProviders array). */
|
|
392
|
+
toolProviders: {
|
|
393
|
+
id: string;
|
|
394
|
+
label?: string;
|
|
395
|
+
description?: string;
|
|
396
|
+
}[];
|
|
397
|
+
/** Registered chat interface ids (host or UI module supplies the list). */
|
|
398
|
+
chatInterfaces: {
|
|
399
|
+
id: string;
|
|
400
|
+
}[];
|
|
401
|
+
logger?: LoggerPort;
|
|
402
|
+
/**
|
|
403
|
+
* Optional pre-auth / post-auth hooks (e.g. shutdown gate, rate
|
|
404
|
+
* limiting). Streaming-specific hooks are not applicable here.
|
|
405
|
+
*/
|
|
406
|
+
hooks?: RouteHooks$1<S>;
|
|
407
|
+
};
|
|
408
|
+
declare function createAdminSettingsRoutes<S>(ctx: AdminSettingsRouteCtx<S>): {
|
|
409
|
+
GET: (req: Request) => Promise<Response>;
|
|
410
|
+
PATCH: (req: Request) => Promise<Response>;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
/** Default ids that match the components shipped in `@firstlovecenter/ai-chat/ui`. */
|
|
414
|
+
declare const BUILTIN_CHAT_INTERFACE_IDS: readonly ["custom", "vercel"];
|
|
415
|
+
type ChatInterfaceRegistryEntry = {
|
|
416
|
+
id: string;
|
|
417
|
+
};
|
|
418
|
+
type ConfigureAiChatOpts<S = unknown> = {
|
|
419
|
+
persistence: PersistencePort;
|
|
420
|
+
auth: AuthPort<S>;
|
|
421
|
+
scope: ScopePort<S>;
|
|
422
|
+
tools: ToolsPort;
|
|
423
|
+
vertex: VertexPort;
|
|
424
|
+
logger?: LoggerPort;
|
|
425
|
+
/**
|
|
426
|
+
* Resolve which narrator drives the prose pass. Defaults to the
|
|
427
|
+
* current `aiSettings.toolProvider` (claude → claude narrator, gemini →
|
|
428
|
+
* gemini narrator). Override when the host stores a per-user choice.
|
|
429
|
+
*/
|
|
430
|
+
resolveNarratorId?: (scope: S) => Promise<'claude' | 'gemini' | 'grok'>;
|
|
431
|
+
/**
|
|
432
|
+
* Chat-interface ids the admin route accepts. Defaults to
|
|
433
|
+
* BUILTIN_CHAT_INTERFACE_IDS (custom, vercel). Hosts that ship only one
|
|
434
|
+
* UI can subset; hosts that register a new one extend.
|
|
435
|
+
*/
|
|
436
|
+
chatInterfaces?: ChatInterfaceRegistryEntry[];
|
|
437
|
+
/**
|
|
438
|
+
* Additional tool-calling providers beyond the built-in claude/gemini.
|
|
439
|
+
* Merged into the registry the admin route validates against.
|
|
440
|
+
*/
|
|
441
|
+
extraToolProviders?: ToolProviderDef[];
|
|
442
|
+
/**
|
|
443
|
+
* Optional lifecycle hooks shared across all three route factories.
|
|
444
|
+
* The full superset (`AgentCustomHooks`) covers the SSE chat route;
|
|
445
|
+
* `chatSessions` and `adminSettings` only consume the
|
|
446
|
+
* `onRequest` / `onAuthenticated` subset.
|
|
447
|
+
*
|
|
448
|
+
* Common host plumbing this enables (without forking the package):
|
|
449
|
+
* - `onRequest` — shutdown gating (503 + Retry-After).
|
|
450
|
+
* - `onAuthenticated` — per-user rate limiting (429 + Retry-After).
|
|
451
|
+
* - `generateSessionId`— project-specific session ids (e.g. SQL view names).
|
|
452
|
+
* - `onSessionStart` — per-request resource setup (e.g. CREATE VIEW).
|
|
453
|
+
* - `onSessionEnd` — cleanup (always-runs, never throws out).
|
|
454
|
+
*/
|
|
455
|
+
hooks?: AgentCustomHooks$1<S>;
|
|
456
|
+
};
|
|
457
|
+
type AiChatRuntime<S = unknown> = {
|
|
458
|
+
/**
|
|
459
|
+
* Pre-bound agent loop. Most hosts call routes instead — but the bound
|
|
460
|
+
* runner is exposed for non-HTTP entry points (jobs, eval harnesses).
|
|
461
|
+
*/
|
|
462
|
+
runAgent: (input: {
|
|
463
|
+
question: string;
|
|
464
|
+
ctx: ToolContext<S>;
|
|
465
|
+
/** Override the provider id picked from `aiSettings.toolProvider`. */
|
|
466
|
+
providerId?: string;
|
|
467
|
+
/** Override the location picked from `aiSettings.gcpLocation`. */
|
|
468
|
+
location?: string;
|
|
469
|
+
maxToolTurns?: number;
|
|
470
|
+
maxOutputTokens?: number;
|
|
471
|
+
}) => Promise<AgentResult>;
|
|
472
|
+
routes: {
|
|
473
|
+
agentCustom: ReturnType<typeof createAgentCustomRoutes<S>>;
|
|
474
|
+
chatSessions: ReturnType<typeof createChatSessionsRoutes<S>>;
|
|
475
|
+
adminSettings: ReturnType<typeof createAdminSettingsRoutes<S>>;
|
|
476
|
+
};
|
|
477
|
+
registries: {
|
|
478
|
+
toolProviders: ToolProviderDef[];
|
|
479
|
+
chatInterfaces: ChatInterfaceRegistryEntry[];
|
|
480
|
+
};
|
|
481
|
+
};
|
|
482
|
+
declare function configureAiChat<S = unknown>(opts: ConfigureAiChatOpts<S>): AiChatRuntime<S>;
|
|
483
|
+
|
|
484
|
+
type RouteHooks<S> = RouteHooks$1<S>;
|
|
485
|
+
type AgentCustomHooks<S> = AgentCustomHooks$1<S>;
|
|
486
|
+
|
|
487
|
+
export { type AgentCustomHooks, type AgentInput, type AgentResult, type AiChatRuntime, AuthPort, BUILTIN_CHAT_INTERFACE_IDS, type ChatInterfaceRegistryEntry, type ConfigureAiChatOpts, DEFAULT_MAX_OUTPUT_TOKENS, DEFAULT_MAX_TOOL_TURNS, LoggerPort, PersistencePort, PresentPayload, type ProviderInitOpts, type RouteHooks, ScopePort, SystemBlock, ToolContext, ToolDefinition, type ToolProviderDef, ToolSchema, ToolsPort, type TranscriptEntry, VertexPort, configureAiChat, getToolProvider, runAgent, toolProviders };
|