@firstlovecenter/ai-chat 0.5.0 → 0.6.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/CHANGELOG.md +32 -0
- package/dist/drizzle/index.cjs +24 -0
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.d.cts +35 -1
- package/dist/drizzle/index.d.ts +35 -1
- package/dist/drizzle/index.js +25 -1
- package/dist/drizzle/index.js.map +1 -1
- package/dist/prisma/index.cjs +7 -0
- package/dist/prisma/index.cjs.map +1 -1
- package/dist/prisma/index.d.cts +7 -1
- package/dist/prisma/index.d.ts +7 -1
- package/dist/prisma/index.js +7 -0
- package/dist/prisma/index.js.map +1 -1
- package/dist/server/index.cjs +47 -12
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +12 -3
- package/dist/server/index.d.ts +12 -3
- package/dist/server/index.js +47 -12
- package/dist/server/index.js.map +1 -1
- package/dist/{types-CDKxdzQc.d.cts → types-CQntnyDJ.d.cts} +15 -2
- package/dist/{types-CDKxdzQc.d.ts → types-CQntnyDJ.d.ts} +15 -2
- package/dist/ui/index.cjs +74 -3
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +74 -3
- package/dist/ui/index.js.map +1 -1
- package/package.json +1 -1
- package/prisma/chat-models.prisma +7 -0
|
@@ -139,16 +139,26 @@ type ChatMessage = {
|
|
|
139
139
|
createdAt: Date;
|
|
140
140
|
};
|
|
141
141
|
/**
|
|
142
|
-
* Singleton row controlling
|
|
143
|
-
* region, and which chat UI (Custom vs. Vercel) renders globally. All three
|
|
142
|
+
* Singleton row controlling global AI runtime settings. All open-string
|
|
144
143
|
* fields are validated at runtime against the registries the host configures
|
|
145
144
|
* — they are not enums in the package's types so consumers can register
|
|
146
145
|
* additional providers / interfaces without a schema change.
|
|
146
|
+
*
|
|
147
|
+
* `maxOutputTokens` caps both the agent loop's per-turn output AND each
|
|
148
|
+
* narrator's prose pass. Reasoning models (e.g. `xai/grok-4.1-fast-reasoning`)
|
|
149
|
+
* charge internal thinking against this budget — set it generously when
|
|
150
|
+
* those are in use.
|
|
151
|
+
*
|
|
152
|
+
* `rolePrompt` is the persona the assistant adopts. When non-null, it
|
|
153
|
+
* takes precedence over the host's static `rolePrompt` configureAiChat
|
|
154
|
+
* option, so admins can edit live in the settings UI.
|
|
147
155
|
*/
|
|
148
156
|
type AiSettings = {
|
|
149
157
|
toolProvider: string;
|
|
150
158
|
gcpLocation: string;
|
|
151
159
|
chatInterface: string;
|
|
160
|
+
maxOutputTokens: number;
|
|
161
|
+
rolePrompt: string | null;
|
|
152
162
|
updatedAt: Date | null;
|
|
153
163
|
updatedByUserId: number | null;
|
|
154
164
|
};
|
|
@@ -242,6 +252,9 @@ type AiSettingsPatch = {
|
|
|
242
252
|
toolProvider?: string;
|
|
243
253
|
gcpLocation?: string;
|
|
244
254
|
chatInterface?: string;
|
|
255
|
+
maxOutputTokens?: number;
|
|
256
|
+
/** Pass `null` to clear back to the host's static fallback. */
|
|
257
|
+
rolePrompt?: string | null;
|
|
245
258
|
};
|
|
246
259
|
/**
|
|
247
260
|
* The whole reason this package is ORM-agnostic. Implemented by:
|
|
@@ -139,16 +139,26 @@ type ChatMessage = {
|
|
|
139
139
|
createdAt: Date;
|
|
140
140
|
};
|
|
141
141
|
/**
|
|
142
|
-
* Singleton row controlling
|
|
143
|
-
* region, and which chat UI (Custom vs. Vercel) renders globally. All three
|
|
142
|
+
* Singleton row controlling global AI runtime settings. All open-string
|
|
144
143
|
* fields are validated at runtime against the registries the host configures
|
|
145
144
|
* — they are not enums in the package's types so consumers can register
|
|
146
145
|
* additional providers / interfaces without a schema change.
|
|
146
|
+
*
|
|
147
|
+
* `maxOutputTokens` caps both the agent loop's per-turn output AND each
|
|
148
|
+
* narrator's prose pass. Reasoning models (e.g. `xai/grok-4.1-fast-reasoning`)
|
|
149
|
+
* charge internal thinking against this budget — set it generously when
|
|
150
|
+
* those are in use.
|
|
151
|
+
*
|
|
152
|
+
* `rolePrompt` is the persona the assistant adopts. When non-null, it
|
|
153
|
+
* takes precedence over the host's static `rolePrompt` configureAiChat
|
|
154
|
+
* option, so admins can edit live in the settings UI.
|
|
147
155
|
*/
|
|
148
156
|
type AiSettings = {
|
|
149
157
|
toolProvider: string;
|
|
150
158
|
gcpLocation: string;
|
|
151
159
|
chatInterface: string;
|
|
160
|
+
maxOutputTokens: number;
|
|
161
|
+
rolePrompt: string | null;
|
|
152
162
|
updatedAt: Date | null;
|
|
153
163
|
updatedByUserId: number | null;
|
|
154
164
|
};
|
|
@@ -242,6 +252,9 @@ type AiSettingsPatch = {
|
|
|
242
252
|
toolProvider?: string;
|
|
243
253
|
gcpLocation?: string;
|
|
244
254
|
chatInterface?: string;
|
|
255
|
+
maxOutputTokens?: number;
|
|
256
|
+
/** Pass `null` to clear back to the host's static fallback. */
|
|
257
|
+
rolePrompt?: string | null;
|
|
245
258
|
};
|
|
246
259
|
/**
|
|
247
260
|
* The whole reason this package is ORM-agnostic. Implemented by:
|
package/dist/ui/index.cjs
CHANGED
|
@@ -684,6 +684,7 @@ function AiChat({
|
|
|
684
684
|
const textareaRef = React.useRef(null);
|
|
685
685
|
const lastAnswerRef = React.useRef(null);
|
|
686
686
|
const prevAnswersLen = React.useRef(0);
|
|
687
|
+
const autoOpenedRef = React.useRef(false);
|
|
687
688
|
React.useLayoutEffect(() => {
|
|
688
689
|
const el = textareaRef.current;
|
|
689
690
|
if (!el) return;
|
|
@@ -698,7 +699,34 @@ function AiChat({
|
|
|
698
699
|
const res = await fetch("/api/chat/sessions", { cache: "no-store" });
|
|
699
700
|
if (!res.ok) return;
|
|
700
701
|
const data = await res.json();
|
|
701
|
-
if (
|
|
702
|
+
if (cancelled) return;
|
|
703
|
+
const list = data.sessions ?? [];
|
|
704
|
+
setSessions(list);
|
|
705
|
+
if (!autoOpenedRef.current && list.length > 0) {
|
|
706
|
+
autoOpenedRef.current = true;
|
|
707
|
+
const mostRecentId = list[0].id;
|
|
708
|
+
setLoadingSession(true);
|
|
709
|
+
setActiveSessionId(mostRecentId);
|
|
710
|
+
try {
|
|
711
|
+
const sres = await fetch(`/api/chat/sessions/${mostRecentId}`, {
|
|
712
|
+
cache: "no-store"
|
|
713
|
+
});
|
|
714
|
+
if (cancelled) return;
|
|
715
|
+
if (!sres.ok) {
|
|
716
|
+
setAnswers([]);
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
const sdata = await sres.json();
|
|
720
|
+
if (cancelled) return;
|
|
721
|
+
setAnswers(messagesToAnswers(sdata.messages ?? []));
|
|
722
|
+
} catch {
|
|
723
|
+
if (!cancelled) setAnswers([]);
|
|
724
|
+
} finally {
|
|
725
|
+
if (!cancelled) setLoadingSession(false);
|
|
726
|
+
}
|
|
727
|
+
} else if (!autoOpenedRef.current) {
|
|
728
|
+
autoOpenedRef.current = true;
|
|
729
|
+
}
|
|
702
730
|
} catch {
|
|
703
731
|
}
|
|
704
732
|
}
|
|
@@ -1475,6 +1503,7 @@ function VercelChat({
|
|
|
1475
1503
|
const textareaRef = React.useRef(null);
|
|
1476
1504
|
const lastAnswerRef = React.useRef(null);
|
|
1477
1505
|
const prevAnswersLen = React.useRef(0);
|
|
1506
|
+
const autoOpenedRef = React.useRef(false);
|
|
1478
1507
|
const activeSessionIdRef = React.useRef(activeSessionId);
|
|
1479
1508
|
const providerRef = React.useRef(provider);
|
|
1480
1509
|
React.useEffect(() => {
|
|
@@ -1529,7 +1558,49 @@ function VercelChat({
|
|
|
1529
1558
|
const res = await fetch("/api/chat/sessions", { cache: "no-store" });
|
|
1530
1559
|
if (!res.ok) return;
|
|
1531
1560
|
const json = await res.json();
|
|
1532
|
-
if (
|
|
1561
|
+
if (cancelled) return;
|
|
1562
|
+
const list = json.sessions ?? [];
|
|
1563
|
+
setSessions(list);
|
|
1564
|
+
if (!autoOpenedRef.current && list.length > 0) {
|
|
1565
|
+
autoOpenedRef.current = true;
|
|
1566
|
+
const mostRecentId = list[0].id;
|
|
1567
|
+
setLoadingSession(true);
|
|
1568
|
+
setActiveSessionId(mostRecentId);
|
|
1569
|
+
try {
|
|
1570
|
+
const sres = await fetch(`/api/chat/sessions/${mostRecentId}`, {
|
|
1571
|
+
cache: "no-store"
|
|
1572
|
+
});
|
|
1573
|
+
if (cancelled) return;
|
|
1574
|
+
if (!sres.ok) {
|
|
1575
|
+
setMessages([]);
|
|
1576
|
+
setHydratedBlocks({});
|
|
1577
|
+
setHydratedProse({});
|
|
1578
|
+
setHydratedErrors({});
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
const sjson = await sres.json();
|
|
1582
|
+
if (cancelled) return;
|
|
1583
|
+
const { uiMessages, blocksMap, proseMap, errorsMap } = storedToUseChat(
|
|
1584
|
+
sjson.messages ?? []
|
|
1585
|
+
);
|
|
1586
|
+
setMessages(uiMessages);
|
|
1587
|
+
setHydratedBlocks(blocksMap);
|
|
1588
|
+
setHydratedProse(proseMap);
|
|
1589
|
+
setHydratedErrors(errorsMap);
|
|
1590
|
+
setStartedAt({});
|
|
1591
|
+
} catch {
|
|
1592
|
+
if (!cancelled) {
|
|
1593
|
+
setMessages([]);
|
|
1594
|
+
setHydratedBlocks({});
|
|
1595
|
+
setHydratedProse({});
|
|
1596
|
+
setHydratedErrors({});
|
|
1597
|
+
}
|
|
1598
|
+
} finally {
|
|
1599
|
+
if (!cancelled) setLoadingSession(false);
|
|
1600
|
+
}
|
|
1601
|
+
} else if (!autoOpenedRef.current) {
|
|
1602
|
+
autoOpenedRef.current = true;
|
|
1603
|
+
}
|
|
1533
1604
|
} catch {
|
|
1534
1605
|
}
|
|
1535
1606
|
}
|
|
@@ -1537,7 +1608,7 @@ function VercelChat({
|
|
|
1537
1608
|
return () => {
|
|
1538
1609
|
cancelled = true;
|
|
1539
1610
|
};
|
|
1540
|
-
}, []);
|
|
1611
|
+
}, [setMessages]);
|
|
1541
1612
|
const answers = React.useMemo(() => {
|
|
1542
1613
|
const liveBlocks = [];
|
|
1543
1614
|
const liveErrors = [];
|