@optilogic/chat 1.0.0-beta.8 → 1.0.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/README.md +136 -0
- package/dist/index.cjs +989 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +361 -2
- package/dist/index.d.ts +361 -2
- package/dist/index.js +964 -46
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/agent-response/AgentResponse.tsx +86 -14
- package/src/components/agent-response/components/HITLSection.tsx +95 -0
- package/src/components/agent-response/components/MetadataRow.tsx +15 -4
- package/src/components/agent-response/components/TruncatedMessage.tsx +52 -0
- package/src/components/agent-response/components/index.ts +6 -0
- package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +65 -8
- package/src/components/agent-response/index.ts +21 -0
- package/src/components/agent-response/types.ts +61 -1
- package/src/components/agent-timeline/AgentTimeline.tsx +256 -0
- package/src/components/agent-timeline/TimelineAgentBlock.tsx +84 -0
- package/src/components/agent-timeline/TimelineItem.tsx +97 -0
- package/src/components/agent-timeline/index.ts +14 -0
- package/src/components/agent-timeline/types.ts +49 -0
- package/src/components/agent-timeline/utils.ts +189 -0
- package/src/components/hitl-interactions/HITLInteractionRecord.tsx +139 -0
- package/src/components/hitl-interactions/HITLQuestionPanel.tsx +270 -0
- package/src/components/hitl-interactions/index.ts +18 -0
- package/src/components/inline-actions/ActionMarkdownRenderer.tsx +60 -0
- package/src/components/inline-actions/index.ts +18 -0
- package/src/components/inline-actions/parseResponseSegments.ts +66 -0
- package/src/components/inline-actions/prompts.ts +41 -0
- package/src/components/inline-actions/types.ts +57 -0
- package/src/components/user-prompt-input/UserPromptInput.tsx +13 -8
- package/src/components/user-prompt-input/types.ts +4 -0
- package/src/index.ts +42 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ResponseSegment } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Regex to match ```json:action fenced code blocks.
|
|
5
|
+
* Captures the JSON content between the fences.
|
|
6
|
+
*/
|
|
7
|
+
const ACTION_BLOCK_REGEX = /```json:action\s*\n([\s\S]*?)```/g;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Parse response text into interleaved markdown and action segments.
|
|
11
|
+
*
|
|
12
|
+
* Finds ```json:action ... ``` fenced code blocks, extracts them as
|
|
13
|
+
* action segments, and returns the surrounding text as markdown segments.
|
|
14
|
+
*
|
|
15
|
+
* Malformed JSON or blocks missing a "type" field are left as markdown
|
|
16
|
+
* (rendered as raw code blocks) for graceful degradation.
|
|
17
|
+
*/
|
|
18
|
+
export function parseResponseSegments(text: string): ResponseSegment[] {
|
|
19
|
+
if (!text) return [];
|
|
20
|
+
|
|
21
|
+
const segments: ResponseSegment[] = [];
|
|
22
|
+
let lastIndex = 0;
|
|
23
|
+
|
|
24
|
+
// Reset regex state (global regexes are stateful)
|
|
25
|
+
ACTION_BLOCK_REGEX.lastIndex = 0;
|
|
26
|
+
|
|
27
|
+
let match: RegExpExecArray | null;
|
|
28
|
+
while ((match = ACTION_BLOCK_REGEX.exec(text)) !== null) {
|
|
29
|
+
// Add markdown segment for text before this match
|
|
30
|
+
const before = text.slice(lastIndex, match.index);
|
|
31
|
+
if (before.trim()) {
|
|
32
|
+
segments.push({ kind: "markdown", content: before });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Try to parse the JSON content
|
|
36
|
+
const jsonContent = match[1].trim();
|
|
37
|
+
let parsed: Record<string, unknown> | null = null;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(jsonContent);
|
|
40
|
+
} catch {
|
|
41
|
+
// Malformed JSON — fall back to markdown
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
|
|
45
|
+
segments.push({
|
|
46
|
+
kind: "action",
|
|
47
|
+
actionType: parsed.type as string,
|
|
48
|
+
payload: parsed,
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
// Missing "type" field or invalid JSON — render as raw code block
|
|
52
|
+
const rawBlock = match[0];
|
|
53
|
+
segments.push({ kind: "markdown", content: rawBlock });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
lastIndex = match.index + match[0].length;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add trailing markdown segment
|
|
60
|
+
const trailing = text.slice(lastIndex);
|
|
61
|
+
if (trailing.trim()) {
|
|
62
|
+
segments.push({ kind: "markdown", content: trailing });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return segments;
|
|
66
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System prompt instructions for agents that emit inline action blocks.
|
|
3
|
+
*
|
|
4
|
+
* Import and append to your agent's system prompt so it knows the
|
|
5
|
+
* json:action format. The XML tags ensure clear boundaries for the LLM.
|
|
6
|
+
*
|
|
7
|
+
* When adding a new action type, add an entry under "Available action types"
|
|
8
|
+
* and create the corresponding React component + registry entry.
|
|
9
|
+
*/
|
|
10
|
+
export const INLINE_ACTION_PROMPT = `
|
|
11
|
+
<inline_actions>
|
|
12
|
+
When your response should include interactive components (like query viewers,
|
|
13
|
+
data tables, or executable actions), embed them as fenced code blocks using
|
|
14
|
+
the \`json:action\` language tag:
|
|
15
|
+
|
|
16
|
+
\`\`\`json:action
|
|
17
|
+
{
|
|
18
|
+
"type": "action-type-here",
|
|
19
|
+
...action-specific fields
|
|
20
|
+
}
|
|
21
|
+
\`\`\`
|
|
22
|
+
|
|
23
|
+
Rules:
|
|
24
|
+
- Each block must contain valid JSON with a "type" field.
|
|
25
|
+
- The "type" must match a registered action component on the frontend.
|
|
26
|
+
- Multiple action blocks per response are allowed.
|
|
27
|
+
- Surround action blocks with normal markdown text for user context.
|
|
28
|
+
- The action block is rendered as an interactive component in the chat UI.
|
|
29
|
+
- SQL strings inside JSON must be properly escaped (newlines as \\n, quotes as \\").
|
|
30
|
+
|
|
31
|
+
Available action types:
|
|
32
|
+
|
|
33
|
+
- "optimap-query": Displays SQL queries with a button to execute them and
|
|
34
|
+
update the 3D globe map.
|
|
35
|
+
Required fields:
|
|
36
|
+
- type: "optimap-query"
|
|
37
|
+
- locations_sql: string (the validated locations SQL query)
|
|
38
|
+
- routes_sql: string (the validated routes SQL query)
|
|
39
|
+
- database_name: string (the target database name)
|
|
40
|
+
</inline_actions>
|
|
41
|
+
`;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ReactNode, ComponentType } from "react";
|
|
2
|
+
|
|
3
|
+
// --- Segment types (output of parser) ---
|
|
4
|
+
|
|
5
|
+
export interface MarkdownSegment {
|
|
6
|
+
kind: "markdown";
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ActionSegment {
|
|
11
|
+
kind: "action";
|
|
12
|
+
actionType: string;
|
|
13
|
+
payload: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ResponseSegment = MarkdownSegment | ActionSegment;
|
|
17
|
+
|
|
18
|
+
// --- Component props ---
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Props passed to every inline action component.
|
|
22
|
+
* T is the shape of the payload for this specific action type.
|
|
23
|
+
*/
|
|
24
|
+
export interface InlineActionProps<T = Record<string, unknown>> {
|
|
25
|
+
/** The parsed payload from the json:action block */
|
|
26
|
+
payload: T;
|
|
27
|
+
/** Callback to send results back to the page (e.g., map data) */
|
|
28
|
+
onAction?: (actionType: string, result: unknown) => void;
|
|
29
|
+
/** True for the most recent agent message; affects button labels */
|
|
30
|
+
isLatest?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// --- Registry ---
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Maps action type strings to React components that render them.
|
|
37
|
+
* The consuming page owns this registry.
|
|
38
|
+
*/
|
|
39
|
+
export type ActionComponentRegistry = Record<
|
|
40
|
+
string,
|
|
41
|
+
ComponentType<InlineActionProps<any>>
|
|
42
|
+
>;
|
|
43
|
+
|
|
44
|
+
// --- Renderer props ---
|
|
45
|
+
|
|
46
|
+
export interface ActionMarkdownRendererProps {
|
|
47
|
+
/** The raw response text (may contain json:action blocks) */
|
|
48
|
+
content: string;
|
|
49
|
+
/** Registry of action type -> component */
|
|
50
|
+
registry: ActionComponentRegistry;
|
|
51
|
+
/** The existing renderMarkdown function for plain markdown segments */
|
|
52
|
+
renderMarkdown: (content: string) => ReactNode;
|
|
53
|
+
/** Callback forwarded to action components */
|
|
54
|
+
onAction?: (actionType: string, result: unknown) => void;
|
|
55
|
+
/** Whether this is the latest (most recent) agent message */
|
|
56
|
+
isLatest?: boolean;
|
|
57
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { Send, Loader2, Square } from "lucide-react";
|
|
3
|
-
import { cn, IconButton } from "@optilogic/core";
|
|
3
|
+
import { cn, IconButton, Tooltip } from "@optilogic/core";
|
|
4
4
|
import {
|
|
5
5
|
SlateEditor,
|
|
6
6
|
Text,
|
|
@@ -143,6 +143,8 @@ export const UserPromptInput = React.forwardRef<
|
|
|
143
143
|
disabled = false,
|
|
144
144
|
isSubmitting = false,
|
|
145
145
|
onStop,
|
|
146
|
+
stopTooltip,
|
|
147
|
+
stopClassName,
|
|
146
148
|
disableWhileSubmitting = true,
|
|
147
149
|
autoFocus = false,
|
|
148
150
|
refocusAfterSubmit = false,
|
|
@@ -294,13 +296,16 @@ export const UserPromptInput = React.forwardRef<
|
|
|
294
296
|
|
|
295
297
|
{/* Send/Stop button */}
|
|
296
298
|
{isSubmitting && onStop ? (
|
|
297
|
-
<
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
299
|
+
<Tooltip content={stopTooltip} disabled={!stopTooltip}>
|
|
300
|
+
<IconButton
|
|
301
|
+
icon={<Square />}
|
|
302
|
+
variant="filled"
|
|
303
|
+
size="sm"
|
|
304
|
+
aria-label={stopTooltip || "Stop"}
|
|
305
|
+
onClick={onStop}
|
|
306
|
+
className={stopClassName}
|
|
307
|
+
/>
|
|
308
|
+
</Tooltip>
|
|
304
309
|
) : (
|
|
305
310
|
<IconButton
|
|
306
311
|
icon={
|
|
@@ -18,6 +18,10 @@ export interface UserPromptInputProps
|
|
|
18
18
|
isSubmitting?: boolean;
|
|
19
19
|
/** Called when user clicks Stop during submission */
|
|
20
20
|
onStop?: () => void;
|
|
21
|
+
/** Tooltip text shown on hover over the stop button */
|
|
22
|
+
stopTooltip?: string;
|
|
23
|
+
/** Additional CSS class names applied to the stop button */
|
|
24
|
+
stopClassName?: string;
|
|
21
25
|
/** Whether to disable input while submitting (default: true) */
|
|
22
26
|
disableWhileSubmitting?: boolean;
|
|
23
27
|
/** Auto-focus the editor when mounted (handles Slate initialization timing) */
|
package/src/index.ts
CHANGED
|
@@ -15,10 +15,14 @@ export {
|
|
|
15
15
|
MetadataRow,
|
|
16
16
|
ThinkingSection,
|
|
17
17
|
ActionBar,
|
|
18
|
+
HITLSection,
|
|
19
|
+
TruncatedMessage,
|
|
18
20
|
type ActivityIndicatorsProps,
|
|
19
21
|
type MetadataRowProps,
|
|
20
22
|
type ThinkingSectionProps,
|
|
21
23
|
type ActionBarProps,
|
|
24
|
+
type HITLSectionProps,
|
|
25
|
+
type TruncatedMessageProps,
|
|
22
26
|
|
|
23
27
|
// Hooks
|
|
24
28
|
useAgentResponseAccumulator,
|
|
@@ -37,6 +41,7 @@ export {
|
|
|
37
41
|
type StatusItem,
|
|
38
42
|
type ThinkingStep,
|
|
39
43
|
type ThinkingContent,
|
|
44
|
+
type PotentialResponse,
|
|
40
45
|
type AgentMessage,
|
|
41
46
|
type GenericWebSocketMessage,
|
|
42
47
|
|
|
@@ -46,6 +51,18 @@ export {
|
|
|
46
51
|
// Utilities
|
|
47
52
|
formatTime,
|
|
48
53
|
formatTotalTime,
|
|
54
|
+
|
|
55
|
+
// Agent Timeline (replaces ThinkingSection for rich execution visibility)
|
|
56
|
+
AgentTimeline,
|
|
57
|
+
createTimelineUIState,
|
|
58
|
+
buildTimelineEntries,
|
|
59
|
+
groupIntoAgentRuns,
|
|
60
|
+
deduplicateEntries,
|
|
61
|
+
type TimelineUIState,
|
|
62
|
+
type TimelineEntry,
|
|
63
|
+
type TimelineEntryType,
|
|
64
|
+
type AgentRun,
|
|
65
|
+
type DisplayEntry,
|
|
49
66
|
} from "./components/agent-response";
|
|
50
67
|
|
|
51
68
|
// User Prompt - Component for displaying user messages
|
|
@@ -57,3 +74,28 @@ export {
|
|
|
57
74
|
type UserPromptInputProps,
|
|
58
75
|
type UserPromptInputRef,
|
|
59
76
|
} from "./components/user-prompt-input";
|
|
77
|
+
|
|
78
|
+
// HITL Interactions - Human-in-the-loop question/response components
|
|
79
|
+
export {
|
|
80
|
+
HITLQuestionPanel,
|
|
81
|
+
type HITLQuestionPanelProps,
|
|
82
|
+
HITLInteractionRecord,
|
|
83
|
+
type HITLInteractionRecordProps,
|
|
84
|
+
type HITLQuestion,
|
|
85
|
+
type HITLInteraction,
|
|
86
|
+
type HITLResponseData,
|
|
87
|
+
buildResponseString,
|
|
88
|
+
} from "./components/hitl-interactions";
|
|
89
|
+
|
|
90
|
+
// Inline Actions - Components for rendering interactive actions within markdown
|
|
91
|
+
export {
|
|
92
|
+
ActionMarkdownRenderer,
|
|
93
|
+
parseResponseSegments,
|
|
94
|
+
INLINE_ACTION_PROMPT,
|
|
95
|
+
type ResponseSegment,
|
|
96
|
+
type MarkdownSegment,
|
|
97
|
+
type ActionSegment,
|
|
98
|
+
type InlineActionProps,
|
|
99
|
+
type ActionComponentRegistry,
|
|
100
|
+
type ActionMarkdownRendererProps,
|
|
101
|
+
} from "./components/inline-actions";
|