@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,268 @@
|
|
|
1
|
+
import { GoogleAuth } from 'google-auth-library';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tool typing for the agent loop.
|
|
5
|
+
*
|
|
6
|
+
* `ToolContext<S>` is the per-request "session" handed to every tool. It
|
|
7
|
+
* carries the resolved Scope (so tools can inject scope filters without
|
|
8
|
+
* trusting their own arguments) and a per-session view suffix (so SQL-ish
|
|
9
|
+
* tools can point the LLM at session-scoped views). The Scope shape itself
|
|
10
|
+
* is opaque to this package — `S` is whatever the host configures.
|
|
11
|
+
*
|
|
12
|
+
* `ToolDefinition` is what we hand to the model adapter plus the function
|
|
13
|
+
* that backs the tool name. The agent loop dispatches on `name` and calls
|
|
14
|
+
* `execute(input, ctx)`; the result is JSON-serialised back to the model.
|
|
15
|
+
*/
|
|
16
|
+
type ToolContext<S = unknown> = {
|
|
17
|
+
/** Host-supplied scope (RBAC / tenancy). The package never inspects the shape. */
|
|
18
|
+
scope: S;
|
|
19
|
+
/** Stable per-conversation id (string). Tools may use this for session-scoped views. */
|
|
20
|
+
sessionId: string;
|
|
21
|
+
/** Short human label for diagnostics — e.g. "Ghana", "all 8 countries". */
|
|
22
|
+
scopeSummary: string;
|
|
23
|
+
/** Number of structured tool calls made before `present` was invoked. */
|
|
24
|
+
toolCallCount: number;
|
|
25
|
+
};
|
|
26
|
+
type ToolSchema = {
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
/** JSON Schema. The package's Vertex Gemini adapter normalises this; the Vercel adapter converts to Zod. */
|
|
30
|
+
input_schema: Record<string, unknown>;
|
|
31
|
+
};
|
|
32
|
+
type ToolResult = {
|
|
33
|
+
ok: true;
|
|
34
|
+
data: unknown;
|
|
35
|
+
} | {
|
|
36
|
+
ok: false;
|
|
37
|
+
error: {
|
|
38
|
+
code: string;
|
|
39
|
+
message: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
type ToolDefinition<I = unknown, S = unknown> = {
|
|
43
|
+
schema: ToolSchema;
|
|
44
|
+
execute(input: I, ctx: ToolContext<S>): Promise<ToolResult>;
|
|
45
|
+
};
|
|
46
|
+
/** The terminal tool that closes the agent loop. The host's tool registry MUST include it. */
|
|
47
|
+
declare const TERMINAL_TOOL_NAME = "present";
|
|
48
|
+
declare function ok(data: unknown): ToolResult;
|
|
49
|
+
declare function err(code: string, message: string): ToolResult;
|
|
50
|
+
type ChartSpec = {
|
|
51
|
+
type: 'line' | 'bar' | 'stacked_bar' | 'pie';
|
|
52
|
+
x: string;
|
|
53
|
+
y: string | string[];
|
|
54
|
+
series?: string;
|
|
55
|
+
};
|
|
56
|
+
type Block = {
|
|
57
|
+
kind: 'paragraph_brief';
|
|
58
|
+
topic: string;
|
|
59
|
+
key_facts: string[];
|
|
60
|
+
} | {
|
|
61
|
+
kind: 'list';
|
|
62
|
+
style: 'bullet' | 'numbered';
|
|
63
|
+
items: string[];
|
|
64
|
+
title?: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'chart';
|
|
67
|
+
title: string;
|
|
68
|
+
spec: ChartSpec;
|
|
69
|
+
data: Array<Record<string, unknown>>;
|
|
70
|
+
} | {
|
|
71
|
+
kind: 'table';
|
|
72
|
+
title: string;
|
|
73
|
+
columns: string[];
|
|
74
|
+
rows: unknown[][];
|
|
75
|
+
} | {
|
|
76
|
+
kind: 'callout';
|
|
77
|
+
tone: 'info' | 'warn' | 'success';
|
|
78
|
+
text: string;
|
|
79
|
+
};
|
|
80
|
+
type PresentPayload = {
|
|
81
|
+
blocks: Block[];
|
|
82
|
+
raw_numbers: Record<string, number | string>;
|
|
83
|
+
};
|
|
84
|
+
type SystemBlock = {
|
|
85
|
+
text: string;
|
|
86
|
+
/**
|
|
87
|
+
* Mark for ephemeral prompt caching where the provider supports it.
|
|
88
|
+
* Anthropic translates this to `cache_control: ephemeral`; Gemini ignores
|
|
89
|
+
* it (Vertex auto-caches stable prefixes). Both still see the same text.
|
|
90
|
+
*/
|
|
91
|
+
cached?: boolean;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Ports — the contract between this package and any host that consumes it.
|
|
96
|
+
*
|
|
97
|
+
* Every project-specific concern (auth, scope, persistence, tools, credentials)
|
|
98
|
+
* crosses the boundary as a typed port the host implements and passes into
|
|
99
|
+
* `configureAiChat({...})`. The package itself has zero knowledge of the
|
|
100
|
+
* host's database, RBAC model, or environment.
|
|
101
|
+
*
|
|
102
|
+
* Domain types (ChatSession, ChatMessage, AiSettings, PresentPayload, Block)
|
|
103
|
+
* are pure TS — no ORM coupling. The Drizzle and Prisma adapters each map
|
|
104
|
+
* their respective row types into these shapes so callers see one contract
|
|
105
|
+
* regardless of the chosen ORM.
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
type ChatSession = {
|
|
109
|
+
id: number;
|
|
110
|
+
userId: number;
|
|
111
|
+
title: string;
|
|
112
|
+
createdAt: Date;
|
|
113
|
+
updatedAt: Date;
|
|
114
|
+
};
|
|
115
|
+
type ChatMessageRole = 'user' | 'assistant';
|
|
116
|
+
/**
|
|
117
|
+
* One message turn. For 'user' rows, `question` carries the raw question text.
|
|
118
|
+
* For 'assistant' rows, `blocks` holds the structured PresentPayload and
|
|
119
|
+
* `prose` holds any paragraph_brief prose collected from the streamed
|
|
120
|
+
* narrator. `errorJson` is set when an assistant turn failed mid-stream.
|
|
121
|
+
*/
|
|
122
|
+
type ChatMessage = {
|
|
123
|
+
id: number;
|
|
124
|
+
sessionId: number;
|
|
125
|
+
role: ChatMessageRole;
|
|
126
|
+
question: string | null;
|
|
127
|
+
blocks: unknown | null;
|
|
128
|
+
prose: unknown | null;
|
|
129
|
+
errorJson: unknown | null;
|
|
130
|
+
createdAt: Date;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Singleton row controlling the active tool-calling provider, the Vertex
|
|
134
|
+
* region, and which chat UI (Custom vs. Vercel) renders globally. All three
|
|
135
|
+
* fields are validated at runtime against the registries the host configures
|
|
136
|
+
* — they are not enums in the package's types so consumers can register
|
|
137
|
+
* additional providers / interfaces without a schema change.
|
|
138
|
+
*/
|
|
139
|
+
type AiSettings = {
|
|
140
|
+
toolProvider: string;
|
|
141
|
+
gcpLocation: string;
|
|
142
|
+
chatInterface: string;
|
|
143
|
+
updatedAt: Date | null;
|
|
144
|
+
updatedByUserId: number | null;
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* The host's resolved Scope (org/tenant/RBAC shape) is opaque to the package.
|
|
148
|
+
* `S` is whatever the host wants — typically a discriminated union with at
|
|
149
|
+
* least `userId: number` plus role/tenancy fields.
|
|
150
|
+
*/
|
|
151
|
+
type AuthOk<S> = {
|
|
152
|
+
ok: true;
|
|
153
|
+
scope: S;
|
|
154
|
+
userId: number;
|
|
155
|
+
};
|
|
156
|
+
type AuthFail = {
|
|
157
|
+
ok: false;
|
|
158
|
+
response: Response;
|
|
159
|
+
};
|
|
160
|
+
type AuthResult<S> = AuthOk<S> | AuthFail;
|
|
161
|
+
type AuthPort<S = unknown> = {
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the calling user from the request. Returning `{ ok: false }`
|
|
164
|
+
* lets the route hand back the prepared 401/403 response untouched.
|
|
165
|
+
*/
|
|
166
|
+
requireAuth(req: Request): Promise<AuthResult<S>>;
|
|
167
|
+
/**
|
|
168
|
+
* Predicate the admin-settings route uses to gate writes. Hosts that
|
|
169
|
+
* don't model admins can return `() => true` if they trust their own
|
|
170
|
+
* routing layer.
|
|
171
|
+
*/
|
|
172
|
+
isSuperAdmin(scope: S): boolean;
|
|
173
|
+
};
|
|
174
|
+
type ScopePort<S = unknown> = {
|
|
175
|
+
/** Short label rendered in the chat UI (e.g. "Ghana", "All countries"). */
|
|
176
|
+
resolveScopeLabel(scope: S): Promise<string>;
|
|
177
|
+
/** Longer narrative the system prompt uses to describe the data slice. */
|
|
178
|
+
buildScopeSummary(scope: S): Promise<string>;
|
|
179
|
+
};
|
|
180
|
+
type ToolsPort = {
|
|
181
|
+
/**
|
|
182
|
+
* Map of tool name → definition. Keys must match `definition.schema.name`.
|
|
183
|
+
* Must include the terminal `present` tool — the agent loop refuses to
|
|
184
|
+
* end a turn without it.
|
|
185
|
+
*/
|
|
186
|
+
tools: Record<string, ToolDefinition>;
|
|
187
|
+
/**
|
|
188
|
+
* Build the ordered list of system blocks for one agent run. The host
|
|
189
|
+
* decides how much of the prompt is project-specific (schema doc,
|
|
190
|
+
* semantic-layer doc, scope summary, etc.). Blocks marked `cached: true`
|
|
191
|
+
* become Anthropic ephemeral cache markers and Vertex prefix-cache hints.
|
|
192
|
+
*/
|
|
193
|
+
buildSystemBlocks(ctx: ToolContext): Promise<SystemBlock[]>;
|
|
194
|
+
};
|
|
195
|
+
type VertexPort = {
|
|
196
|
+
projectId: string;
|
|
197
|
+
/** e.g. 'us-east5' or 'global' — used unless `aiSettings.gcpLocation` overrides per-request. */
|
|
198
|
+
defaultLocation: string;
|
|
199
|
+
/**
|
|
200
|
+
* A constructed `GoogleAuth` instance. Any auth scheme that yields one
|
|
201
|
+
* works (ADC, Workload Identity Federation, split-key env vars,
|
|
202
|
+
* Secret Manager, etc.). The package never reads `process.env.GCP_*`.
|
|
203
|
+
*/
|
|
204
|
+
auth: GoogleAuth;
|
|
205
|
+
/** Vertex model IDs pinned by the host (orgs pin their own versions). */
|
|
206
|
+
modelIds: {
|
|
207
|
+
claude: string;
|
|
208
|
+
gemini: string;
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
type LoggerPort = {
|
|
212
|
+
debug(...args: unknown[]): void;
|
|
213
|
+
info(...args: unknown[]): void;
|
|
214
|
+
warn(...args: unknown[]): void;
|
|
215
|
+
error(...args: unknown[]): void;
|
|
216
|
+
};
|
|
217
|
+
type CreateSessionInput = {
|
|
218
|
+
userId: number;
|
|
219
|
+
title: string;
|
|
220
|
+
};
|
|
221
|
+
type AppendMessageInput = {
|
|
222
|
+
sessionId: number;
|
|
223
|
+
role: ChatMessageRole;
|
|
224
|
+
question?: string | null;
|
|
225
|
+
blocks?: unknown | null;
|
|
226
|
+
prose?: unknown | null;
|
|
227
|
+
errorJson?: unknown | null;
|
|
228
|
+
};
|
|
229
|
+
type ListSessionsOpts = {
|
|
230
|
+
limit?: number;
|
|
231
|
+
};
|
|
232
|
+
type AiSettingsPatch = {
|
|
233
|
+
toolProvider?: string;
|
|
234
|
+
gcpLocation?: string;
|
|
235
|
+
chatInterface?: string;
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* The whole reason this package is ORM-agnostic. Implemented by:
|
|
239
|
+
* - createDrizzlePersistence (src/adapters/drizzle/)
|
|
240
|
+
* - createPrismaPersistence (src/adapters/prisma/)
|
|
241
|
+
* - createMemoryPersistence (src/adapters/memory/, internal test use)
|
|
242
|
+
*
|
|
243
|
+
* Routes, agent loop, and UI never touch ORMs directly — they only call
|
|
244
|
+
* methods on this port. A parameterized contract test runs the same
|
|
245
|
+
* assertions against all three adapters to keep them in sync.
|
|
246
|
+
*/
|
|
247
|
+
type PersistencePort = {
|
|
248
|
+
createSession(input: CreateSessionInput): Promise<ChatSession>;
|
|
249
|
+
/** Returns null when the session doesn't exist OR doesn't belong to this user. */
|
|
250
|
+
getSession(id: number, userId: number): Promise<ChatSession | null>;
|
|
251
|
+
listSessionsForUser(userId: number, opts?: ListSessionsOpts): Promise<ChatSession[]>;
|
|
252
|
+
/** No-op when session doesn't exist or doesn't belong to user — caller asserted authorisation. */
|
|
253
|
+
updateSession(id: number, userId: number, patch: {
|
|
254
|
+
title?: string;
|
|
255
|
+
}): Promise<void>;
|
|
256
|
+
deleteSession(id: number, userId: number): Promise<void>;
|
|
257
|
+
appendMessage(input: AppendMessageInput): Promise<ChatMessage>;
|
|
258
|
+
listMessagesForSession(sessionId: number, userId: number): Promise<ChatMessage[]>;
|
|
259
|
+
/** Returns the singleton row, applying defaults if it's never been written. */
|
|
260
|
+
getAiSettings(): Promise<AiSettings>;
|
|
261
|
+
/**
|
|
262
|
+
* Upsert. Validates `toolProvider` and `chatInterface` against the runtime
|
|
263
|
+
* registries before writing — invalid values throw before SQL is issued.
|
|
264
|
+
*/
|
|
265
|
+
updateAiSettings(patch: AiSettingsPatch, byUserId: number): Promise<AiSettings>;
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
export { type AuthPort as A, type Block as B, type ChartSpec as C, type LoggerPort as L, type PresentPayload as P, type SystemBlock as S, type ToolSchema as T, type VertexPort as V, type ToolContext as a, type ToolDefinition as b, type PersistencePort as c, type ScopePort as d, type ToolsPort as e, type AiSettings as f, type AiSettingsPatch as g, type AppendMessageInput as h, type AuthFail as i, type AuthOk as j, type AuthResult as k, type ChatMessage as l, type ChatMessageRole as m, type ChatSession as n, type CreateSessionInput as o, type ListSessionsOpts as p, TERMINAL_TOOL_NAME as q, type ToolResult as r, err as s, ok as t };
|