@opengeni/react 3.7.0-canary.6 → 3.7.0-canary.7
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/README.md +43 -3
- package/dist/artifacts.js +7 -7
- package/dist/{browser-viewer-NTD6UIPR.js → browser-viewer-SS46LUAC.js} +3 -3
- package/dist/chat.d.ts +37 -0
- package/dist/chat.js +315 -0
- package/dist/chat.js.map +1 -0
- package/dist/{chunk-LUYB4SRW.js → chunk-3HIF5IFC.js} +1048 -2439
- package/dist/chunk-3HIF5IFC.js.map +1 -0
- package/dist/{chunk-3IY6UZI6.js → chunk-BQGIXRKV.js} +288 -396
- package/dist/chunk-BQGIXRKV.js.map +1 -0
- package/dist/{chunk-MOWYEBCQ.js → chunk-BXJOQFCD.js} +5 -4
- package/dist/chunk-BXJOQFCD.js.map +1 -0
- package/dist/{chunk-BV5A4OTE.js → chunk-ELIYWBZR.js} +4 -4
- package/dist/chunk-F2CFHN3Y.js +1415 -0
- package/dist/chunk-F2CFHN3Y.js.map +1 -0
- package/dist/chunk-HQV2I5HV.js +124 -0
- package/dist/chunk-HQV2I5HV.js.map +1 -0
- package/dist/{chunk-JT33KZZN.js → chunk-R4KRSFGA.js} +11 -20
- package/dist/chunk-R4KRSFGA.js.map +1 -0
- package/dist/{chunk-KWEZ4MPF.js → chunk-UMECRJRV.js} +63 -60
- package/dist/chunk-UMECRJRV.js.map +1 -0
- package/dist/{chunk-24M4YBCL.js → chunk-VEPNNDNG.js} +4 -4
- package/dist/{chunk-WMGF4Z7X.js → chunk-VRBL3F5E.js} +23 -3
- package/dist/{chunk-WMGF4Z7X.js.map → chunk-VRBL3F5E.js.map} +1 -1
- package/dist/components/model-policy-picker.d.ts +4 -0
- package/dist/components/sandbox-files.d.ts +3 -1
- package/dist/composer.js +6 -5
- package/dist/{computer-viewer-7TEZC6Y2.js → computer-viewer-FWEZISW4.js} +3 -3
- package/dist/hooks/use-session-events.d.ts +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +92 -36
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +8 -8
- package/dist/{model-policy-picker-menu-R6WXNWJL.js → model-policy-picker-menu-3R336DP2.js} +4 -4
- package/dist/model-policy.d.ts +2 -0
- package/dist/model-policy.js +3 -1
- package/dist/realtime.js +5 -5
- package/dist/session-ui.js +11 -8
- package/dist/session.js +1 -1
- package/package.json +6 -2
- package/src/chat.tsx +393 -0
- package/src/components/model-policy-picker-menu.tsx +92 -71
- package/src/components/model-policy-picker.tsx +21 -18
- package/src/components/sandbox-files.tsx +90 -14
- package/src/components/sandbox-workspace.tsx +16 -1
- package/src/hooks/use-session-events.ts +3 -0
- package/src/hooks/use-session.ts +10 -3
- package/src/index.ts +1 -0
- package/src/model-policy.ts +26 -2
- package/styles/compiled.css +40 -8
- package/styles/tokens.css +2 -2
- package/dist/chunk-3IY6UZI6.js.map +0 -1
- package/dist/chunk-JT33KZZN.js.map +0 -1
- package/dist/chunk-KWEZ4MPF.js.map +0 -1
- package/dist/chunk-LUYB4SRW.js.map +0 -1
- package/dist/chunk-MOWYEBCQ.js.map +0 -1
- /package/dist/{browser-viewer-NTD6UIPR.js.map → browser-viewer-SS46LUAC.js.map} +0 -0
- /package/dist/{chunk-BV5A4OTE.js.map → chunk-ELIYWBZR.js.map} +0 -0
- /package/dist/{chunk-24M4YBCL.js.map → chunk-VEPNNDNG.js.map} +0 -0
- /package/dist/{computer-viewer-7TEZC6Y2.js.map → computer-viewer-FWEZISW4.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-R6WXNWJL.js.map → model-policy-picker-menu-3R336DP2.js.map} +0 -0
package/README.md
CHANGED
|
@@ -24,6 +24,32 @@ ancestor. Components are styled with Tailwind v4 utilities mapped onto the
|
|
|
24
24
|
tokens, Radix primitives for behavior, and Motion for state-communicating
|
|
25
25
|
animation. Override the tokens to rebrand everything.
|
|
26
26
|
|
|
27
|
+
## Drop-in chat (`@opengeni/react/chat`)
|
|
28
|
+
|
|
29
|
+
The smallest surface. `OpenGeniChat` talks only to your own endpoint served by
|
|
30
|
+
`createChatHandler` from `@opengeni/sdk/chat`; it needs no provider, no SDK
|
|
31
|
+
client, and no proxy of OpenGeni routes:
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { OpenGeniChat } from "@opengeni/react/chat";
|
|
35
|
+
|
|
36
|
+
<OpenGeniChat handlerUrl="/api/chat" conversation="c_9" placeholder="Ask anything" />;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
It posts `{ message }`, renders the streamed reply with `Markdown`, and shows a
|
|
40
|
+
card with buttons when the agent stops for an approval or a structured
|
|
41
|
+
human-input request (answered through `POST /api/chat/respond`). Optional props:
|
|
42
|
+
`headers` for the host's auth header, `authKey`, `className`, and `renderMessage`.
|
|
43
|
+
Changed header values clear messages, pending cards, drafts and in-flight
|
|
44
|
+
requests before restoring the new identity's history. Header callbacks are
|
|
45
|
+
resolved on each host render; re-render when their credentials change. For
|
|
46
|
+
cookie authentication, pass `authKey={JSON.stringify([tenantId, userId])}` and
|
|
47
|
+
change it on sign-in, sign-out and tenant switches, even when `conversation`
|
|
48
|
+
stays the same. `authKey` is a local reset key and is never sent to the handler.
|
|
49
|
+
`SessionConversation` below is the full-surface upgrade on the same session
|
|
50
|
+
(files, queue, model picker, approvals with policies) once the host proxies the
|
|
51
|
+
session routes.
|
|
52
|
+
|
|
27
53
|
## Editable Office artifacts
|
|
28
54
|
|
|
29
55
|
The optional artifact workbench is isolated from the ordinary session and
|
|
@@ -722,6 +748,16 @@ capability document so every surface degrades to a reason instead of crashing.
|
|
|
722
748
|
These surfaces pull in [optional peer dependencies](#optional-peer-dependencies)
|
|
723
749
|
— install only the ones for surfaces you actually mount.
|
|
724
750
|
|
|
751
|
+
`SandboxWorkspace` keeps capture-backed file browsing passive, but an explicit
|
|
752
|
+
live-file open acquires a viewer. Failed opens show the connection error and a
|
|
753
|
+
retry that renegotiates the viewer instead of leaving a waking spinner running.
|
|
754
|
+
When composing `SandboxFiles` directly, pass `workspaceError` alongside
|
|
755
|
+
`liveWorkspaceReady` and supply an `onWakeWorkspace` callback that can retry a
|
|
756
|
+
failed negotiation. Complete PNG, JPEG, GIF, and WebP reads render as read-only
|
|
757
|
+
image previews; truncated reads and other binary formats remain non-editable
|
|
758
|
+
notices. Image previews use the existing bounded file-read path and do not
|
|
759
|
+
publish or retain additional files.
|
|
760
|
+
|
|
725
761
|
## Connected Machines (`@opengeni/react/machines`)
|
|
726
762
|
|
|
727
763
|
Bring-your-own-compute UI: the Machines dashboard, per-machine metrics, the
|
|
@@ -793,14 +829,18 @@ consumer described above, with deterministic mock and same-origin live modes.
|
|
|
793
829
|
### Model selection
|
|
794
830
|
|
|
795
831
|
`ModelPolicyPicker` opens a flat, searchable list grouped by payment source, with
|
|
796
|
-
|
|
832
|
+
the selected model checked in its provider group. Choosing a model applies it and closes the popover.
|
|
797
833
|
Thinking and supported speed controls remain in a fixed footer instead of a
|
|
798
834
|
nested page. Model changes preserve supported reasoning effort and latency;
|
|
799
835
|
unsupported effort falls back to the new model's default, and unsupported speed
|
|
800
|
-
returns to Standard.
|
|
801
|
-
|
|
836
|
+
returns to Standard. Thinking uses inline radio choices and is hidden when the
|
|
837
|
+
model has no adjustable reasoning levels. Availability and Codex-only session restrictions still disable choices.
|
|
802
838
|
|
|
803
839
|
The trigger renders immediately; the searchable popover loads when opened. Hosts
|
|
804
840
|
can translate its search, current-selection, empty-result, attachment-warning, and
|
|
805
841
|
thinking labels through `messages`, and override payment descriptions through
|
|
806
842
|
`messages.billingHints`.
|
|
843
|
+
|
|
844
|
+
Subscription descriptions appear once per provider group. Free models carry a
|
|
845
|
+
Free badge. Pass `hasImageAttachments` for the current draft to show an image
|
|
846
|
+
compatibility warning only when the selected model cannot view those images.
|
package/dist/artifacts.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EditablePresentationArtifactSurface,
|
|
3
|
+
PresentationArtifactSurface,
|
|
4
|
+
PresentationEditor,
|
|
5
|
+
PresentationProjectionArtifactSurface,
|
|
6
|
+
PresentationProjectionEditor
|
|
7
|
+
} from "./chunk-PEI6RANI.js";
|
|
1
8
|
import {
|
|
2
9
|
DocumentArtifactSurface,
|
|
3
10
|
DocumentEditor,
|
|
@@ -12,13 +19,6 @@ import {
|
|
|
12
19
|
SpreadsheetGrid,
|
|
13
20
|
SpreadsheetProjectionGrid
|
|
14
21
|
} from "./chunk-ICA6R6TW.js";
|
|
15
|
-
import {
|
|
16
|
-
EditablePresentationArtifactSurface,
|
|
17
|
-
PresentationArtifactSurface,
|
|
18
|
-
PresentationEditor,
|
|
19
|
-
PresentationProjectionArtifactSurface,
|
|
20
|
-
PresentationProjectionEditor
|
|
21
|
-
} from "./chunk-PEI6RANI.js";
|
|
22
22
|
import {
|
|
23
23
|
ArtifactSurface,
|
|
24
24
|
EditableArtifactMessage
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BrowserViewer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VEPNNDNG.js";
|
|
4
4
|
import "./chunk-H5YGYNXS.js";
|
|
5
5
|
import "./chunk-5YK2ZDIG.js";
|
|
6
|
-
import "./chunk-JVYQRRC4.js";
|
|
7
6
|
import "./chunk-PLYRL5ER.js";
|
|
8
7
|
import "./chunk-KZ73RL76.js";
|
|
9
8
|
import "./chunk-NEXLLTP4.js";
|
|
9
|
+
import "./chunk-JVYQRRC4.js";
|
|
10
10
|
import "./chunk-22KP6LR5.js";
|
|
11
11
|
export {
|
|
12
12
|
BrowserViewer
|
|
13
13
|
};
|
|
14
|
-
//# sourceMappingURL=browser-viewer-
|
|
14
|
+
//# sourceMappingURL=browser-viewer-SS46LUAC.js.map
|
package/dist/chat.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Chat component that talks only to the host's `createChatHandler` endpoint:
|
|
4
|
+
* it restores the conversation with `GET` on mount, POSTs `{ message }`
|
|
5
|
+
* (native format), renders the streamed reply, shows a card for a pending
|
|
6
|
+
* approval or human-input request, and answers it through
|
|
7
|
+
* `${handlerUrl}/respond`. No provider or OpenGeni credentials in the browser.
|
|
8
|
+
*/
|
|
9
|
+
export type OpenGeniChatMessage = {
|
|
10
|
+
id: string;
|
|
11
|
+
role: "user" | "assistant";
|
|
12
|
+
text: string;
|
|
13
|
+
streaming: boolean;
|
|
14
|
+
/** Tool names the agent used while producing this reply. */
|
|
15
|
+
tools: string[];
|
|
16
|
+
};
|
|
17
|
+
export type OpenGeniChatProps = {
|
|
18
|
+
/** The host endpoint served by `createChatHandler`. */
|
|
19
|
+
handlerUrl: string;
|
|
20
|
+
/** Sent as the `x-opengeni-conversation` header so the host's `resolve` can pick it up. */
|
|
21
|
+
conversation?: string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Host authentication headers, resolved on each host render. Changed values
|
|
24
|
+
* reset the chat. Re-render the host when a callback's credentials change.
|
|
25
|
+
*/
|
|
26
|
+
headers?: Record<string, string> | (() => Record<string, string>) | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Stable authenticated user/tenant identity. Change this on sign-in, sign-out
|
|
29
|
+
* or tenant switches when authentication uses cookies or otherwise changes
|
|
30
|
+
* without changing the headers. This resets local state; it is not sent.
|
|
31
|
+
*/
|
|
32
|
+
authKey?: string | undefined;
|
|
33
|
+
placeholder?: string | undefined;
|
|
34
|
+
className?: string | undefined;
|
|
35
|
+
renderMessage?: ((message: OpenGeniChatMessage) => ReactNode) | undefined;
|
|
36
|
+
};
|
|
37
|
+
export declare function OpenGeniChat(props: OpenGeniChatProps): import("react").JSX.Element;
|
package/dist/chat.js
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HumanInputForm,
|
|
3
|
+
Markdown
|
|
4
|
+
} from "./chunk-F2CFHN3Y.js";
|
|
5
|
+
import "./chunk-JVYQRRC4.js";
|
|
6
|
+
import "./chunk-HQV2I5HV.js";
|
|
7
|
+
import "./chunk-VZTQ73XT.js";
|
|
8
|
+
import "./chunk-22KP6LR5.js";
|
|
9
|
+
|
|
10
|
+
// src/chat.tsx
|
|
11
|
+
import {
|
|
12
|
+
CHAT_CONVERSATION_HEADER,
|
|
13
|
+
CHAT_FORMAT_HEADER,
|
|
14
|
+
parseChatChunkStream
|
|
15
|
+
} from "@opengeni/sdk/chat";
|
|
16
|
+
import {
|
|
17
|
+
useCallback,
|
|
18
|
+
useEffect,
|
|
19
|
+
useRef,
|
|
20
|
+
useState
|
|
21
|
+
} from "react";
|
|
22
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
23
|
+
var styles = {
|
|
24
|
+
root: {
|
|
25
|
+
display: "flex",
|
|
26
|
+
flexDirection: "column",
|
|
27
|
+
gap: "0.75rem",
|
|
28
|
+
minHeight: 0,
|
|
29
|
+
color: "var(--og-color-fg)",
|
|
30
|
+
fontFamily: "var(--og-font-sans)",
|
|
31
|
+
fontSize: "var(--og-font-size-base)"
|
|
32
|
+
},
|
|
33
|
+
list: { display: "flex", flexDirection: "column", gap: "0.5rem", overflowY: "auto" },
|
|
34
|
+
user: {
|
|
35
|
+
alignSelf: "flex-end",
|
|
36
|
+
maxWidth: "80%",
|
|
37
|
+
whiteSpace: "pre-wrap",
|
|
38
|
+
padding: "0.5rem 0.75rem",
|
|
39
|
+
borderRadius: "var(--og-radius-lg)",
|
|
40
|
+
background: "var(--og-color-accent-soft)"
|
|
41
|
+
},
|
|
42
|
+
assistant: { alignSelf: "stretch", padding: "0.25rem 0" },
|
|
43
|
+
meta: { color: "var(--og-color-fg-muted)", fontSize: "var(--og-font-size-xs)" },
|
|
44
|
+
card: {
|
|
45
|
+
display: "flex",
|
|
46
|
+
flexDirection: "column",
|
|
47
|
+
gap: "0.5rem",
|
|
48
|
+
padding: "0.75rem",
|
|
49
|
+
border: "1px solid var(--og-color-border)",
|
|
50
|
+
borderRadius: "var(--og-radius-md)",
|
|
51
|
+
background: "var(--og-color-surface-2)"
|
|
52
|
+
},
|
|
53
|
+
row: { display: "flex", gap: "0.5rem", alignItems: "flex-end" },
|
|
54
|
+
input: {
|
|
55
|
+
flex: 1,
|
|
56
|
+
minHeight: "2.5rem",
|
|
57
|
+
padding: "0.5rem 0.75rem",
|
|
58
|
+
border: "1px solid var(--og-color-border)",
|
|
59
|
+
borderRadius: "var(--og-radius-md)",
|
|
60
|
+
background: "var(--og-color-surface-1)",
|
|
61
|
+
color: "inherit",
|
|
62
|
+
font: "inherit",
|
|
63
|
+
resize: "vertical"
|
|
64
|
+
},
|
|
65
|
+
button: {
|
|
66
|
+
padding: "0.5rem 0.9rem",
|
|
67
|
+
border: "1px solid var(--og-color-border-strong)",
|
|
68
|
+
borderRadius: "var(--og-radius-md)",
|
|
69
|
+
background: "var(--og-color-accent)",
|
|
70
|
+
color: "var(--og-color-accent-fg)",
|
|
71
|
+
font: "inherit",
|
|
72
|
+
cursor: "pointer"
|
|
73
|
+
},
|
|
74
|
+
secondary: {
|
|
75
|
+
padding: "0.5rem 0.9rem",
|
|
76
|
+
border: "1px solid var(--og-color-border)",
|
|
77
|
+
borderRadius: "var(--og-radius-md)",
|
|
78
|
+
background: "var(--og-color-surface-1)",
|
|
79
|
+
color: "inherit",
|
|
80
|
+
font: "inherit",
|
|
81
|
+
cursor: "pointer"
|
|
82
|
+
},
|
|
83
|
+
error: { color: "var(--og-color-danger)", fontSize: "var(--og-font-size-sm)" }
|
|
84
|
+
};
|
|
85
|
+
function uid() {
|
|
86
|
+
return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
87
|
+
}
|
|
88
|
+
function requestHeaders(conversation, headers) {
|
|
89
|
+
return {
|
|
90
|
+
[CHAT_FORMAT_HEADER]: "native",
|
|
91
|
+
...conversation ? { [CHAT_CONVERSATION_HEADER]: conversation } : {},
|
|
92
|
+
...headers
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function restoredMessages(payload) {
|
|
96
|
+
const list = payload?.messages;
|
|
97
|
+
if (!Array.isArray(list)) return [];
|
|
98
|
+
const restored = [];
|
|
99
|
+
for (const entry of list) {
|
|
100
|
+
const record = entry ?? {};
|
|
101
|
+
if (record.role !== "user" && record.role !== "assistant" || typeof record.text !== "string")
|
|
102
|
+
continue;
|
|
103
|
+
restored.push({ id: uid(), role: record.role, text: record.text, streaming: false, tools: [] });
|
|
104
|
+
}
|
|
105
|
+
return restored;
|
|
106
|
+
}
|
|
107
|
+
function OpenGeniChat(props) {
|
|
108
|
+
const headers = Object.fromEntries(
|
|
109
|
+
[...new Headers(typeof props.headers === "function" ? props.headers() : props.headers)].map(([name, value]) => [name.toLowerCase(), value]).sort(([left], [right]) => left.localeCompare(right))
|
|
110
|
+
);
|
|
111
|
+
const identity = JSON.stringify([
|
|
112
|
+
props.handlerUrl,
|
|
113
|
+
props.conversation ?? null,
|
|
114
|
+
props.authKey ?? null,
|
|
115
|
+
headers
|
|
116
|
+
]);
|
|
117
|
+
return /* @__PURE__ */ jsx(ChatConversation, { ...props, headers }, identity);
|
|
118
|
+
}
|
|
119
|
+
function ChatConversation({
|
|
120
|
+
handlerUrl,
|
|
121
|
+
conversation,
|
|
122
|
+
headers,
|
|
123
|
+
placeholder = "Message",
|
|
124
|
+
className,
|
|
125
|
+
renderMessage
|
|
126
|
+
}) {
|
|
127
|
+
const [messages, setMessages] = useState([]);
|
|
128
|
+
const [pendingRequests, setPendingRequests] = useState([]);
|
|
129
|
+
const pending = pendingRequests[0] ?? null;
|
|
130
|
+
const [restoring, setRestoring] = useState(true);
|
|
131
|
+
const [draft, setDraft] = useState("");
|
|
132
|
+
const [busy, setBusy] = useState(false);
|
|
133
|
+
const [error, setError] = useState(null);
|
|
134
|
+
const abortRef = useRef(null);
|
|
135
|
+
const headersRef = useRef(headers);
|
|
136
|
+
headersRef.current = headers;
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
const controller = new AbortController();
|
|
139
|
+
abortRef.current?.abort();
|
|
140
|
+
setBusy(false);
|
|
141
|
+
setRestoring(true);
|
|
142
|
+
setError(null);
|
|
143
|
+
setMessages([]);
|
|
144
|
+
setPendingRequests([]);
|
|
145
|
+
void (async () => {
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(handlerUrl, {
|
|
148
|
+
method: "GET",
|
|
149
|
+
headers: requestHeaders(conversation, headersRef.current),
|
|
150
|
+
signal: controller.signal
|
|
151
|
+
});
|
|
152
|
+
if (!response.ok) return;
|
|
153
|
+
const payload = await response.json();
|
|
154
|
+
if (controller.signal.aborted) return;
|
|
155
|
+
setMessages(restoredMessages(payload));
|
|
156
|
+
setPendingRequests(Array.isArray(payload.pending) ? payload.pending : []);
|
|
157
|
+
} catch {
|
|
158
|
+
} finally {
|
|
159
|
+
if (!controller.signal.aborted) setRestoring(false);
|
|
160
|
+
}
|
|
161
|
+
})();
|
|
162
|
+
return () => {
|
|
163
|
+
controller.abort();
|
|
164
|
+
abortRef.current?.abort();
|
|
165
|
+
};
|
|
166
|
+
}, [handlerUrl, conversation]);
|
|
167
|
+
const update = useCallback(
|
|
168
|
+
(id, patch) => setMessages((prev) => prev.map((message) => message.id === id ? patch(message) : message)),
|
|
169
|
+
[]
|
|
170
|
+
);
|
|
171
|
+
const consume = useCallback(
|
|
172
|
+
async (url, body, assistantId) => {
|
|
173
|
+
abortRef.current?.abort();
|
|
174
|
+
const controller = new AbortController();
|
|
175
|
+
abortRef.current = controller;
|
|
176
|
+
setBusy(true);
|
|
177
|
+
setError(null);
|
|
178
|
+
try {
|
|
179
|
+
const response = await fetch(url, {
|
|
180
|
+
method: "POST",
|
|
181
|
+
headers: { "Content-Type": "application/json", ...requestHeaders(conversation, headers) },
|
|
182
|
+
body: JSON.stringify(body),
|
|
183
|
+
signal: controller.signal
|
|
184
|
+
});
|
|
185
|
+
if (!response.ok || !response.body) {
|
|
186
|
+
throw new Error(`Chat request failed (${response.status}).`);
|
|
187
|
+
}
|
|
188
|
+
for await (const chunk of parseChatChunkStream(response.body)) {
|
|
189
|
+
if (controller.signal.aborted) return;
|
|
190
|
+
if (chunk.type === "text") {
|
|
191
|
+
update(assistantId, (m) => ({ ...m, text: m.text + chunk.text }));
|
|
192
|
+
} else if (chunk.type === "tool" && chunk.status === "started") {
|
|
193
|
+
update(assistantId, (m) => ({ ...m, tools: [...m.tools, chunk.name] }));
|
|
194
|
+
} else if (chunk.type === "pending") {
|
|
195
|
+
setPendingRequests([chunk.pending]);
|
|
196
|
+
} else if (chunk.type === "done") {
|
|
197
|
+
setPendingRequests(chunk.reply.pending ? [chunk.reply.pending] : []);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch (caught) {
|
|
201
|
+
if (!controller.signal.aborted && !(caught instanceof Error && caught.name === "AbortError")) {
|
|
202
|
+
setError(caught instanceof Error ? caught.message : "Chat request failed.");
|
|
203
|
+
}
|
|
204
|
+
} finally {
|
|
205
|
+
if (!controller.signal.aborted) {
|
|
206
|
+
update(assistantId, (m) => ({ ...m, streaming: false }));
|
|
207
|
+
setBusy(false);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
[conversation, headers, update]
|
|
212
|
+
);
|
|
213
|
+
const submit = (event) => {
|
|
214
|
+
event.preventDefault();
|
|
215
|
+
const text = draft.trim();
|
|
216
|
+
if (!text || busy || restoring) return;
|
|
217
|
+
setDraft("");
|
|
218
|
+
const assistantId = uid();
|
|
219
|
+
setMessages((prev) => [
|
|
220
|
+
...prev,
|
|
221
|
+
{ id: uid(), role: "user", text, streaming: false, tools: [] },
|
|
222
|
+
{ id: assistantId, role: "assistant", text: "", streaming: true, tools: [] }
|
|
223
|
+
]);
|
|
224
|
+
void consume(handlerUrl, { message: text }, assistantId);
|
|
225
|
+
};
|
|
226
|
+
const respond = (input) => {
|
|
227
|
+
if (!pending || busy || restoring) return;
|
|
228
|
+
const last = [...messages].reverse().find((m) => m.role === "assistant");
|
|
229
|
+
const assistantId = last?.id ?? uid();
|
|
230
|
+
if (last) update(assistantId, (m) => ({ ...m, streaming: true }));
|
|
231
|
+
else
|
|
232
|
+
setMessages((prev) => [
|
|
233
|
+
...prev,
|
|
234
|
+
{ id: assistantId, role: "assistant", text: "", streaming: true, tools: [] }
|
|
235
|
+
]);
|
|
236
|
+
void consume(
|
|
237
|
+
`${handlerUrl.replace(/\/+$/, "")}/respond`,
|
|
238
|
+
{ requestId: pending.requestId, ...input },
|
|
239
|
+
assistantId
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
return /* @__PURE__ */ jsxs("div", { className: `og-root og-chat${className ? ` ${className}` : ""}`, style: styles.root, children: [
|
|
243
|
+
/* @__PURE__ */ jsx("div", { className: "og-chat-messages", style: styles.list, children: messages.map(
|
|
244
|
+
(message) => renderMessage ? /* @__PURE__ */ jsx("div", { children: renderMessage(message) }, message.id) : message.role === "user" ? /* @__PURE__ */ jsx("div", { className: "og-chat-user", style: styles.user, children: message.text }, message.id) : /* @__PURE__ */ jsxs("div", { className: "og-chat-assistant", style: styles.assistant, children: [
|
|
245
|
+
message.tools.length > 0 ? /* @__PURE__ */ jsxs("div", { style: styles.meta, children: [
|
|
246
|
+
"Used ",
|
|
247
|
+
message.tools.join(", ")
|
|
248
|
+
] }) : null,
|
|
249
|
+
message.text ? /* @__PURE__ */ jsx(Markdown, { streaming: message.streaming, children: message.text }) : message.streaming ? /* @__PURE__ */ jsx("div", { style: styles.meta, children: "Thinking" }) : null
|
|
250
|
+
] }, message.id)
|
|
251
|
+
) }),
|
|
252
|
+
pending ? /* @__PURE__ */ jsx("div", { className: "og-chat-pending", style: styles.card, children: pending.kind === "approval" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
253
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
254
|
+
"The agent wants to run ",
|
|
255
|
+
pending.name ?? "a tool",
|
|
256
|
+
"."
|
|
257
|
+
] }),
|
|
258
|
+
/* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
259
|
+
/* @__PURE__ */ jsx(
|
|
260
|
+
"button",
|
|
261
|
+
{
|
|
262
|
+
type: "button",
|
|
263
|
+
style: styles.button,
|
|
264
|
+
onClick: () => respond({ decision: "approve" }),
|
|
265
|
+
children: "Approve"
|
|
266
|
+
}
|
|
267
|
+
),
|
|
268
|
+
/* @__PURE__ */ jsx(
|
|
269
|
+
"button",
|
|
270
|
+
{
|
|
271
|
+
type: "button",
|
|
272
|
+
style: styles.secondary,
|
|
273
|
+
onClick: () => respond({ decision: "reject" }),
|
|
274
|
+
children: "Reject"
|
|
275
|
+
}
|
|
276
|
+
)
|
|
277
|
+
] })
|
|
278
|
+
] }) : /* @__PURE__ */ jsx(
|
|
279
|
+
HumanInputForm,
|
|
280
|
+
{
|
|
281
|
+
request: pending.payload,
|
|
282
|
+
submitting: busy || restoring,
|
|
283
|
+
error,
|
|
284
|
+
onSubmit: (response) => respond(
|
|
285
|
+
response.outcome === "answered" ? { answers: response.answers } : { skip: true }
|
|
286
|
+
)
|
|
287
|
+
}
|
|
288
|
+
) }) : null,
|
|
289
|
+
error ? /* @__PURE__ */ jsx("div", { className: "og-chat-error", style: styles.error, children: error }) : null,
|
|
290
|
+
/* @__PURE__ */ jsxs("form", { className: "og-chat-composer", style: styles.row, onSubmit: submit, children: [
|
|
291
|
+
/* @__PURE__ */ jsx(
|
|
292
|
+
"textarea",
|
|
293
|
+
{
|
|
294
|
+
style: styles.input,
|
|
295
|
+
value: draft,
|
|
296
|
+
placeholder,
|
|
297
|
+
rows: 1,
|
|
298
|
+
onChange: (e) => setDraft(e.target.value),
|
|
299
|
+
onInput: (e) => setDraft(e.currentTarget.value),
|
|
300
|
+
onKeyDown: (e) => {
|
|
301
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
302
|
+
e.preventDefault();
|
|
303
|
+
e.currentTarget.form?.requestSubmit();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
),
|
|
308
|
+
/* @__PURE__ */ jsx("button", { type: "submit", style: styles.button, disabled: busy || restoring || !draft.trim(), children: "Send" })
|
|
309
|
+
] })
|
|
310
|
+
] });
|
|
311
|
+
}
|
|
312
|
+
export {
|
|
313
|
+
OpenGeniChat
|
|
314
|
+
};
|
|
315
|
+
//# sourceMappingURL=chat.js.map
|
package/dist/chat.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chat.tsx"],"sourcesContent":["import {\n CHAT_CONVERSATION_HEADER,\n CHAT_FORMAT_HEADER,\n parseChatChunkStream,\n type ChatPending,\n} from \"@opengeni/sdk/chat\";\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type FormEvent,\n type ReactNode,\n} from \"react\";\nimport { HumanInputForm, type HumanInputFormProps } from \"./components/human-input-form\";\nimport { Markdown } from \"./components/markdown\";\n\n/**\n * Chat component that talks only to the host's `createChatHandler` endpoint:\n * it restores the conversation with `GET` on mount, POSTs `{ message }`\n * (native format), renders the streamed reply, shows a card for a pending\n * approval or human-input request, and answers it through\n * `${handlerUrl}/respond`. No provider or OpenGeni credentials in the browser.\n */\n\nexport type OpenGeniChatMessage = {\n id: string;\n role: \"user\" | \"assistant\";\n text: string;\n streaming: boolean;\n /** Tool names the agent used while producing this reply. */\n tools: string[];\n};\n\nexport type OpenGeniChatProps = {\n /** The host endpoint served by `createChatHandler`. */\n handlerUrl: string;\n /** Sent as the `x-opengeni-conversation` header so the host's `resolve` can pick it up. */\n conversation?: string | undefined;\n /**\n * Host authentication headers, resolved on each host render. Changed values\n * reset the chat. Re-render the host when a callback's credentials change.\n */\n headers?: Record<string, string> | (() => Record<string, string>) | undefined;\n /**\n * Stable authenticated user/tenant identity. Change this on sign-in, sign-out\n * or tenant switches when authentication uses cookies or otherwise changes\n * without changing the headers. This resets local state; it is not sent.\n */\n authKey?: string | undefined;\n placeholder?: string | undefined;\n className?: string | undefined;\n renderMessage?: ((message: OpenGeniChatMessage) => ReactNode) | undefined;\n};\n\nconst styles = {\n root: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.75rem\",\n minHeight: 0,\n color: \"var(--og-color-fg)\",\n fontFamily: \"var(--og-font-sans)\",\n fontSize: \"var(--og-font-size-base)\",\n },\n list: { display: \"flex\", flexDirection: \"column\", gap: \"0.5rem\", overflowY: \"auto\" },\n user: {\n alignSelf: \"flex-end\",\n maxWidth: \"80%\",\n whiteSpace: \"pre-wrap\",\n padding: \"0.5rem 0.75rem\",\n borderRadius: \"var(--og-radius-lg)\",\n background: \"var(--og-color-accent-soft)\",\n },\n assistant: { alignSelf: \"stretch\", padding: \"0.25rem 0\" },\n meta: { color: \"var(--og-color-fg-muted)\", fontSize: \"var(--og-font-size-xs)\" },\n card: {\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"0.5rem\",\n padding: \"0.75rem\",\n border: \"1px solid var(--og-color-border)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-surface-2)\",\n },\n row: { display: \"flex\", gap: \"0.5rem\", alignItems: \"flex-end\" },\n input: {\n flex: 1,\n minHeight: \"2.5rem\",\n padding: \"0.5rem 0.75rem\",\n border: \"1px solid var(--og-color-border)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-surface-1)\",\n color: \"inherit\",\n font: \"inherit\",\n resize: \"vertical\",\n },\n button: {\n padding: \"0.5rem 0.9rem\",\n border: \"1px solid var(--og-color-border-strong)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-accent)\",\n color: \"var(--og-color-accent-fg)\",\n font: \"inherit\",\n cursor: \"pointer\",\n },\n secondary: {\n padding: \"0.5rem 0.9rem\",\n border: \"1px solid var(--og-color-border)\",\n borderRadius: \"var(--og-radius-md)\",\n background: \"var(--og-color-surface-1)\",\n color: \"inherit\",\n font: \"inherit\",\n cursor: \"pointer\",\n },\n error: { color: \"var(--og-color-danger)\", fontSize: \"var(--og-font-size-sm)\" },\n} satisfies Record<string, CSSProperties>;\n\nfunction uid(): string {\n return typeof crypto !== \"undefined\" && \"randomUUID\" in crypto\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n}\n\ntype HostHeaders = Record<string, string>;\n\nfunction requestHeaders(conversation: string | undefined, headers: HostHeaders) {\n return {\n [CHAT_FORMAT_HEADER]: \"native\",\n ...(conversation ? { [CHAT_CONVERSATION_HEADER]: conversation } : {}),\n ...headers,\n };\n}\n\n/** Messages from the handler's `GET` history response; anything unexpected is dropped. */\nfunction restoredMessages(payload: unknown): OpenGeniChatMessage[] {\n const list = (payload as { messages?: unknown } | null)?.messages;\n if (!Array.isArray(list)) return [];\n const restored: OpenGeniChatMessage[] = [];\n for (const entry of list) {\n const record = (entry ?? {}) as { role?: unknown; text?: unknown };\n if ((record.role !== \"user\" && record.role !== \"assistant\") || typeof record.text !== \"string\")\n continue;\n restored.push({ id: uid(), role: record.role, text: record.text, streaming: false, tools: [] });\n }\n return restored;\n}\n\nexport function OpenGeniChat(props: OpenGeniChatProps) {\n // Normalize actual HTTP values so an inline object/callback or header casing\n // change does not erase a draft. Resolve callbacks once per host render: GET\n // and POST must use the same authentication snapshot as the displayed state.\n const headers = Object.fromEntries(\n [...new Headers(typeof props.headers === \"function\" ? props.headers() : props.headers)]\n .map(([name, value]) => [name.toLowerCase(), value] as const)\n .sort(([left], [right]) => left.localeCompare(right)),\n );\n const identity = JSON.stringify([\n props.handlerUrl,\n props.conversation ?? null,\n props.authKey ?? null,\n headers,\n ]);\n // A keyed boundary drops private state in the same commit, before effects\n // run. Unmount cleanup aborts old history and streams; late completions can\n // only address the unmounted instance, never the new user's conversation.\n return <ChatConversation key={identity} {...props} headers={headers} />;\n}\n\nfunction ChatConversation({\n handlerUrl,\n conversation,\n headers,\n placeholder = \"Message\",\n className,\n renderMessage,\n}: Omit<OpenGeniChatProps, \"headers\"> & { headers: HostHeaders }) {\n const [messages, setMessages] = useState<OpenGeniChatMessage[]>([]);\n const [pendingRequests, setPendingRequests] = useState<ChatPending[]>([]);\n const pending = pendingRequests[0] ?? null;\n const [restoring, setRestoring] = useState(true);\n const [draft, setDraft] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const headersRef = useRef<HostHeaders>(headers);\n headersRef.current = headers;\n\n // Restore the conversation on mount and whenever it changes. The composer\n // waits for restoration so a late snapshot cannot resurrect a decided card.\n useEffect(() => {\n const controller = new AbortController();\n abortRef.current?.abort();\n setBusy(false);\n setRestoring(true);\n setError(null);\n setMessages([]);\n setPendingRequests([]);\n void (async () => {\n try {\n const response = await fetch(handlerUrl, {\n method: \"GET\",\n headers: requestHeaders(conversation, headersRef.current),\n signal: controller.signal,\n });\n if (!response.ok) return;\n const payload = (await response.json()) as { pending?: ChatPending[] };\n if (controller.signal.aborted) return;\n setMessages(restoredMessages(payload));\n setPendingRequests(Array.isArray(payload.pending) ? payload.pending : []);\n } catch {\n // Hosts without history may still accept sends.\n } finally {\n if (!controller.signal.aborted) setRestoring(false);\n }\n })();\n return () => {\n controller.abort();\n abortRef.current?.abort();\n };\n }, [handlerUrl, conversation]);\n\n const update = useCallback(\n (id: string, patch: (message: OpenGeniChatMessage) => OpenGeniChatMessage) =>\n setMessages((prev) => prev.map((message) => (message.id === id ? patch(message) : message))),\n [],\n );\n\n const consume = useCallback(\n async (url: string, body: unknown, assistantId: string) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n setBusy(true);\n setError(null);\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\", ...requestHeaders(conversation, headers) },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n if (!response.ok || !response.body) {\n throw new Error(`Chat request failed (${response.status}).`);\n }\n for await (const chunk of parseChatChunkStream(response.body)) {\n if (controller.signal.aborted) return;\n if (chunk.type === \"text\") {\n update(assistantId, (m) => ({ ...m, text: m.text + chunk.text }));\n } else if (chunk.type === \"tool\" && chunk.status === \"started\") {\n update(assistantId, (m) => ({ ...m, tools: [...m.tools, chunk.name] }));\n } else if (chunk.type === \"pending\") {\n setPendingRequests([chunk.pending]);\n } else if (chunk.type === \"done\") {\n setPendingRequests(chunk.reply.pending ? [chunk.reply.pending] : []);\n }\n }\n } catch (caught) {\n if (\n !controller.signal.aborted &&\n !(caught instanceof Error && caught.name === \"AbortError\")\n ) {\n setError(caught instanceof Error ? caught.message : \"Chat request failed.\");\n }\n } finally {\n if (!controller.signal.aborted) {\n update(assistantId, (m) => ({ ...m, streaming: false }));\n setBusy(false);\n }\n }\n },\n [conversation, headers, update],\n );\n\n const submit = (event: FormEvent) => {\n event.preventDefault();\n const text = draft.trim();\n if (!text || busy || restoring) return;\n setDraft(\"\");\n const assistantId = uid();\n setMessages((prev) => [\n ...prev,\n { id: uid(), role: \"user\", text, streaming: false, tools: [] },\n { id: assistantId, role: \"assistant\", text: \"\", streaming: true, tools: [] },\n ]);\n void consume(handlerUrl, { message: text }, assistantId);\n };\n\n const respond = (input: Record<string, unknown>) => {\n if (!pending || busy || restoring) return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const assistantId = last?.id ?? uid();\n if (last) update(assistantId, (m) => ({ ...m, streaming: true }));\n else\n setMessages((prev) => [\n ...prev,\n { id: assistantId, role: \"assistant\", text: \"\", streaming: true, tools: [] },\n ]);\n void consume(\n `${handlerUrl.replace(/\\/+$/, \"\")}/respond`,\n { requestId: pending.requestId, ...input },\n assistantId,\n );\n };\n\n return (\n <div className={`og-root og-chat${className ? ` ${className}` : \"\"}`} style={styles.root}>\n <div className=\"og-chat-messages\" style={styles.list}>\n {messages.map((message) =>\n renderMessage ? (\n <div key={message.id}>{renderMessage(message)}</div>\n ) : message.role === \"user\" ? (\n <div key={message.id} className=\"og-chat-user\" style={styles.user}>\n {message.text}\n </div>\n ) : (\n <div key={message.id} className=\"og-chat-assistant\" style={styles.assistant}>\n {message.tools.length > 0 ? (\n <div style={styles.meta}>Used {message.tools.join(\", \")}</div>\n ) : null}\n {message.text ? (\n <Markdown streaming={message.streaming}>{message.text}</Markdown>\n ) : message.streaming ? (\n <div style={styles.meta}>Thinking</div>\n ) : null}\n </div>\n ),\n )}\n </div>\n {pending ? (\n <div className=\"og-chat-pending\" style={styles.card}>\n {pending.kind === \"approval\" ? (\n <>\n <div>The agent wants to run {pending.name ?? \"a tool\"}.</div>\n <div style={styles.row}>\n <button\n type=\"button\"\n style={styles.button}\n onClick={() => respond({ decision: \"approve\" })}\n >\n Approve\n </button>\n <button\n type=\"button\"\n style={styles.secondary}\n onClick={() => respond({ decision: \"reject\" })}\n >\n Reject\n </button>\n </div>\n </>\n ) : (\n <HumanInputForm\n request={pending.payload as HumanInputFormProps[\"request\"]}\n submitting={busy || restoring}\n error={error}\n onSubmit={(response) =>\n respond(\n response.outcome === \"answered\" ? { answers: response.answers } : { skip: true },\n )\n }\n />\n )}\n </div>\n ) : null}\n {error ? (\n <div className=\"og-chat-error\" style={styles.error}>\n {error}\n </div>\n ) : null}\n <form className=\"og-chat-composer\" style={styles.row} onSubmit={submit}>\n <textarea\n style={styles.input}\n value={draft}\n placeholder={placeholder}\n rows={1}\n onChange={(e) => setDraft(e.target.value)}\n onInput={(e) => setDraft(e.currentTarget.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n e.currentTarget.form?.requestSubmit();\n }\n }}\n />\n <button type=\"submit\" style={styles.button} disabled={busy || restoring || !draft.trim()}>\n Send\n </button>\n </form>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|