@robosystems/core 0.8.9 → 0.9.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/components/index.d.ts +1 -0
- package/components/index.js +1 -0
- package/components/report-chat/ReportChat.d.ts +29 -0
- package/components/report-chat/ReportChat.js +101 -0
- package/components/report-chat/anchor.d.ts +9 -0
- package/components/report-chat/anchor.js +18 -0
- package/components/report-chat/index.d.ts +2 -0
- package/components/report-chat/index.js +2 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +1 -1
package/components/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { EntitySelectorCore, type EntityLike, type EntityRecord, type EntitySele
|
|
|
4
4
|
export { GraphFilters, byGraphType, composeFilters, excludeRepositories, excludeSubgraphs, hasAllSchemaExtensions, hasAnySchemaExtension, hasSchemaExtension, onlyEntityGraphs, onlyGenericGraphs, onlyRepositories, onlyUserGraphs, } from './graph-filters';
|
|
5
5
|
export { GraphSelectorCore, type GraphSelectorProps } from './GraphSelectorCore';
|
|
6
6
|
export { PageLayout } from './PageLayout';
|
|
7
|
+
export { ReportChat, anchoredMessage, errorDetail, type ReportChatProps, } from './report-chat';
|
|
7
8
|
export { ActiveSubscriptions, BrowseRepositories, type ActiveSubscriptionsProps, type BrowseRepositoriesProps, } from './repositories';
|
|
8
9
|
export { RepositoryGuard, useIsRepository } from './RepositoryGuard';
|
|
9
10
|
export { SearchBar, SearchContent, SearchHitCard, SearchPagination, SearchResultsMeta, } from './search';
|
package/components/index.js
CHANGED
|
@@ -4,6 +4,7 @@ export { EntitySelectorCore, } from './EntitySelectorCore';
|
|
|
4
4
|
export { GraphFilters, byGraphType, composeFilters, excludeRepositories, excludeSubgraphs, hasAllSchemaExtensions, hasAnySchemaExtension, hasSchemaExtension, onlyEntityGraphs, onlyGenericGraphs, onlyRepositories, onlyUserGraphs, } from './graph-filters';
|
|
5
5
|
export { GraphSelectorCore } from './GraphSelectorCore';
|
|
6
6
|
export { PageLayout } from './PageLayout';
|
|
7
|
+
export { ReportChat, anchoredMessage, errorDetail, } from './report-chat';
|
|
7
8
|
export { ActiveSubscriptions, BrowseRepositories, } from './repositories';
|
|
8
9
|
export { RepositoryGuard, useIsRepository } from './RepositoryGuard';
|
|
9
10
|
export { SearchBar, SearchContent, SearchHitCard, SearchPagination, SearchResultsMeta, } from './search';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface ReportChatProps {
|
|
3
|
+
/** The graph the operator runs on. */
|
|
4
|
+
graphId: string;
|
|
5
|
+
/** The note that anchors the operator on the report on screen. */
|
|
6
|
+
anchorNote: string;
|
|
7
|
+
/** The same report's identifiers, sent as `context.focus`. */
|
|
8
|
+
focus?: Record<string, unknown>;
|
|
9
|
+
/** Card title. */
|
|
10
|
+
title?: string;
|
|
11
|
+
/** Short text at the right of the header, e.g. what answers and what it costs. */
|
|
12
|
+
hint?: string;
|
|
13
|
+
/** The empty-state paragraph above the example questions. */
|
|
14
|
+
intro?: ReactNode;
|
|
15
|
+
/** Tap-to-ask questions on the empty state. */
|
|
16
|
+
examples?: string[];
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
/**
|
|
19
|
+
* When false the body shows `unavailable` instead of the chat — for a user
|
|
20
|
+
* without access to the graph, say. The header still renders, so the card
|
|
21
|
+
* reads the same wherever it sits.
|
|
22
|
+
*/
|
|
23
|
+
available?: boolean;
|
|
24
|
+
unavailable?: ReactNode;
|
|
25
|
+
defaultOpen?: boolean;
|
|
26
|
+
/** Operator mode; `standard` gives the loop enough tool turns for a schema read, a query or two, and an answer. */
|
|
27
|
+
mode?: 'quick' | 'standard' | 'extended';
|
|
28
|
+
}
|
|
29
|
+
export declare function ReportChat({ graphId, anchorNote, focus, title, hint, intro, examples, placeholder, available, unavailable, defaultOpen, mode, }: ReportChatProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Ask about this report — a collapsible chat card that sends questions to the
|
|
5
|
+
* RoboSystems operator on one graph, anchored on the report the host page
|
|
6
|
+
* has on screen. No key to enter: the call is the account's own, through the
|
|
7
|
+
* SDK's operator client (the same one the Console uses), and it spends the
|
|
8
|
+
* account's credits, which each answer shows.
|
|
9
|
+
*
|
|
10
|
+
* The host decides the anchor. RoboInvestor passes a filer and a filing on
|
|
11
|
+
* the SEC graph; RoboLedger passes an entity and a report on its own graph.
|
|
12
|
+
* Both hand the same shape: a note the operator reads ahead of every
|
|
13
|
+
* question, and a `focus` object with the same identifiers for an operator
|
|
14
|
+
* release that reads `context` directly.
|
|
15
|
+
*/
|
|
16
|
+
import { Spinner } from 'flowbite-react';
|
|
17
|
+
import { useCallback, useRef, useState } from 'react';
|
|
18
|
+
import { HiChevronDown, HiChevronRight, HiSparkles } from 'react-icons/hi';
|
|
19
|
+
import ReactMarkdown from 'react-markdown';
|
|
20
|
+
import remarkGfm from 'remark-gfm';
|
|
21
|
+
import { anchoredMessage, errorDetail } from './anchor';
|
|
22
|
+
const MARKDOWN_CLASS = 'text-sm leading-relaxed text-zinc-800 dark:text-zinc-200 [&_a]:text-primary-600 dark:[&_a]:text-primary-400 [&_a]:underline [&_code]:rounded [&_code]:bg-zinc-100 [&_code]:px-1 dark:[&_code]:bg-zinc-800 [&_h1]:mt-3 [&_h1]:text-base [&_h1]:font-semibold [&_h2]:mt-3 [&_h2]:text-base [&_h2]:font-semibold [&_h3]:mt-2 [&_h3]:font-semibold [&_li]:my-0.5 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:my-2 [&_strong]:font-semibold [&_table]:my-2 [&_table]:text-xs [&_td]:px-2 [&_td]:py-1 [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_ul]:list-disc [&_ul]:pl-5';
|
|
23
|
+
export function ReportChat({ graphId, anchorNote, focus, title = 'Ask about this report', hint, intro, examples = [], placeholder = 'Ask…', available = true, unavailable, defaultOpen = false, mode = 'standard', }) {
|
|
24
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
25
|
+
const [turns, setTurns] = useState([]);
|
|
26
|
+
const [draft, setDraft] = useState('');
|
|
27
|
+
const [busy, setBusy] = useState(false);
|
|
28
|
+
const [progress, setProgress] = useState('');
|
|
29
|
+
const scrollRef = useRef(null);
|
|
30
|
+
const ask = useCallback(async (question) => {
|
|
31
|
+
var _a;
|
|
32
|
+
const q = question.trim();
|
|
33
|
+
if (!q || busy)
|
|
34
|
+
return;
|
|
35
|
+
setTurns((prev) => [...prev, { role: 'user', text: q }]);
|
|
36
|
+
setBusy(true);
|
|
37
|
+
setProgress('Thinking');
|
|
38
|
+
try {
|
|
39
|
+
const { clients } = await import('@robosystems/client/clients');
|
|
40
|
+
// Prior turns as the operator saw them: the questions without the
|
|
41
|
+
// anchor, which rides on every current message, and the answers.
|
|
42
|
+
const history = turns.map((t) => ({ role: t.role, content: t.text }));
|
|
43
|
+
const result = await clients.operator.executeQuery(graphId, Object.assign(Object.assign({ message: anchoredMessage(anchorNote, q), mode }, (history.length > 0 ? { history } : {})), (focus ? { context: { focus } } : {})), {
|
|
44
|
+
mode: 'auto',
|
|
45
|
+
onProgress: (message, percentage) => setProgress(percentage !== undefined
|
|
46
|
+
? `${message} (${percentage}%)`
|
|
47
|
+
: message),
|
|
48
|
+
});
|
|
49
|
+
// The API reports operator failures (credit pre-flight, timeouts) as
|
|
50
|
+
// a normal response with `error_details` set; render those as errors.
|
|
51
|
+
if (result.error_details) {
|
|
52
|
+
setTurns((prev) => [
|
|
53
|
+
...prev,
|
|
54
|
+
{
|
|
55
|
+
role: 'assistant',
|
|
56
|
+
text: errorDetail(result.error_details, result.content ||
|
|
57
|
+
'The operator could not complete this request.'),
|
|
58
|
+
error: true,
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const credits = ((_a = result.metadata) !== null && _a !== void 0 ? _a : {})['credits_consumed'];
|
|
64
|
+
setTurns((prev) => [
|
|
65
|
+
...prev,
|
|
66
|
+
{
|
|
67
|
+
role: 'assistant',
|
|
68
|
+
text: result.content || '(no answer returned)',
|
|
69
|
+
credits: typeof credits === 'number' ? credits : null,
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
setTurns((prev) => [
|
|
75
|
+
...prev,
|
|
76
|
+
{
|
|
77
|
+
role: 'assistant',
|
|
78
|
+
text: `Error: ${e instanceof Error ? e.message : String(e)}`,
|
|
79
|
+
error: true,
|
|
80
|
+
},
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
setBusy(false);
|
|
85
|
+
setProgress('');
|
|
86
|
+
requestAnimationFrame(() => {
|
|
87
|
+
const el = scrollRef.current;
|
|
88
|
+
if (el)
|
|
89
|
+
el.scrollTop = el.scrollHeight;
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}, [busy, turns, graphId, anchorNote, focus, mode]);
|
|
93
|
+
return (_jsxs("section", { className: "mb-8 rounded-lg border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-900/60", children: [_jsxs("button", { type: "button", onClick: () => setOpen((o) => !o), "aria-expanded": open, className: "flex w-full items-center gap-2 px-4 py-3 text-left", children: [open ? (_jsx(HiChevronDown, { className: "h-4 w-4 shrink-0 text-zinc-400" })) : (_jsx(HiChevronRight, { className: "h-4 w-4 shrink-0 text-zinc-400" })), _jsx(HiSparkles, { className: "text-primary-500 h-4 w-4 shrink-0" }), _jsx("span", { className: "font-semibold text-zinc-900 dark:text-white", children: title }), hint ? (_jsx("span", { className: "ml-auto hidden text-xs text-zinc-500 sm:inline dark:text-zinc-400", children: hint })) : null] }), open && !available ? (_jsx("div", { className: "border-t border-zinc-200 px-4 py-4 text-sm text-zinc-600 dark:border-zinc-800 dark:text-zinc-400", children: unavailable })) : null, open && available ? (_jsxs("div", { className: "border-t border-zinc-200 dark:border-zinc-800", children: [_jsxs("div", { ref: scrollRef, className: "max-h-[28rem] space-y-3 overflow-y-auto px-4 py-4", children: [turns.length === 0 ? (_jsxs("div", { className: "text-sm text-zinc-600 dark:text-zinc-400", children: [intro ? _jsx("p", { children: intro }) : null, examples.length > 0 ? (_jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: examples.map((q) => (_jsx("button", { type: "button", disabled: busy, onClick: () => void ask(q), className: "rounded-full border border-zinc-300 px-3 py-1 text-xs text-zinc-700 hover:bg-zinc-50 disabled:opacity-50 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800", children: q }, q))) })) : null] })) : (turns.map((turn, i) => turn.role === 'user' ? (_jsx("div", { className: "ml-auto max-w-[85%] rounded-lg bg-zinc-100 px-3 py-2 text-sm text-zinc-900 dark:bg-zinc-800 dark:text-zinc-100", children: turn.text }, i)) : (_jsxs("div", { className: `max-w-[95%] rounded-lg px-3 py-2 ${turn.error
|
|
94
|
+
? 'border border-red-200 bg-red-50 text-sm text-red-700 dark:border-red-900/50 dark:bg-red-900/20 dark:text-red-400'
|
|
95
|
+
: 'bg-zinc-50 dark:bg-zinc-950/40'}`, children: [turn.error ? (turn.text) : (_jsx("div", { className: MARKDOWN_CLASS, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: turn.text }) })), typeof turn.credits === 'number' ? (_jsxs("div", { className: "mt-1 text-xs text-zinc-400", children: [turn.credits.toFixed(1), " credits"] })) : null] }, i)))), busy ? (_jsxs("div", { className: "flex items-center gap-2 text-sm text-zinc-500 dark:text-zinc-400", children: [_jsx(Spinner, { size: "sm" }), " ", progress || 'Thinking', "\u2026"] })) : null] }), _jsxs("form", { className: "flex gap-2 border-t border-zinc-200 px-4 py-3 dark:border-zinc-800", onSubmit: (e) => {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
const q = draft;
|
|
98
|
+
setDraft('');
|
|
99
|
+
void ask(q);
|
|
100
|
+
}, children: [_jsx("input", { type: "text", value: draft, onChange: (e) => setDraft(e.target.value), placeholder: placeholder, disabled: busy, "aria-label": title, className: "focus:border-primary-500 focus:ring-primary-500 flex-1 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 placeholder-zinc-400 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:placeholder-zinc-500" }), _jsx("button", { type: "submit", disabled: busy || !draft.trim(), className: "bg-primary-600 hover:bg-primary-700 rounded-lg px-4 py-2 text-sm font-medium text-white disabled:opacity-50", children: "Ask" })] })] })) : null] }));
|
|
101
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message sent for one question: the anchor note the host page wrote
|
|
3
|
+
* for the report on screen, then the question. The note rides on every
|
|
4
|
+
* message because the operator does not yet read `context.focus` into its
|
|
5
|
+
* prompt; when it does, hosts can pass `focus` alone and an empty note.
|
|
6
|
+
*/
|
|
7
|
+
export declare function anchoredMessage(anchorNote: string, question: string): string;
|
|
8
|
+
/** The first non-empty string among an operator failure's fields. */
|
|
9
|
+
export declare function errorDetail(details: Record<string, unknown> | undefined, fallback: string): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message sent for one question: the anchor note the host page wrote
|
|
3
|
+
* for the report on screen, then the question. The note rides on every
|
|
4
|
+
* message because the operator does not yet read `context.focus` into its
|
|
5
|
+
* prompt; when it does, hosts can pass `focus` alone and an empty note.
|
|
6
|
+
*/
|
|
7
|
+
export function anchoredMessage(anchorNote, question) {
|
|
8
|
+
const q = question.trim();
|
|
9
|
+
const note = anchorNote.trim();
|
|
10
|
+
return note ? `${note}\n\nQuestion: ${q}` : q;
|
|
11
|
+
}
|
|
12
|
+
/** The first non-empty string among an operator failure's fields. */
|
|
13
|
+
export function errorDetail(details, fallback) {
|
|
14
|
+
if (!details)
|
|
15
|
+
return fallback;
|
|
16
|
+
const found = [details.message, details.error, details.detail].find((v) => typeof v === 'string' && v.length > 0);
|
|
17
|
+
return found !== null && found !== void 0 ? found : fallback;
|
|
18
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type { Entities, Entity, User } from './types';
|
|
|
12
12
|
export { customTheme } from './theme';
|
|
13
13
|
export { buildGraphAwareConsoleConfig, ConsoleContent, EXAMPLE_SETS, getGraphExampleKind, useGraphAwareConsoleConfig, } from './components/console';
|
|
14
14
|
export type { ConsoleBranding, ConsoleCommandContext, ConsoleConfig, ConsoleExtraCommand, ConsoleHeaderConfig, ConsoleMcpConfig, ConsoleWelcomeConfig, GraphExampleKind, GraphExampleSet, SampleQuery, } from './components/console';
|
|
15
|
+
export { anchoredMessage, errorDetail, ReportChat, type ReportChatProps, } from './components/report-chat';
|
|
15
16
|
export { ActiveSubscriptions, BrowseRepositories, EntitySelector, EntitySelectorCore, GraphSelectorCore, PageLayout, RepositoryGuard, SearchBar, SearchContent, SearchHitCard, SearchPagination, SearchResultsMeta, useIsRepository, type ActiveSubscriptionsProps, type BrowseRepositoriesProps, type EntityGroup, type EntityLike, type EntityRecord, type EntitySelectorProps, type GraphSelectorProps, type GraphWithEntities, type SearchBarProps, type SearchConfig, type SearchFilterConfig, type SearchHitCardProps, type SearchPaginationProps, type SearchResultsMetaProps, type SelectableEntity, } from './components';
|
|
16
17
|
export { composeFilters, excludeRepositories, excludeSubgraphs, GraphFilters, hasSchemaExtension, onlyRepositories, onlyUserGraphs, } from './components/graph-filters';
|
|
17
18
|
export { ApiKeyDisplay, BrandSpinner, categorizeError, CategoryInput, ChatHeader, ChatInputArea, ChatMessage, ConfirmModal, CoreNavbar, CoreSidebar, CreateApiKeyModal, DeepResearchToggle, EmptyState, ErrorType, GeneralInformationCard, generateMessageId, getErrorMessage, LandingFooter, LoadingState, MarkdownProse, PageContainer, PageHeader, PasswordInformationCard, PasswordRequirements, SecureApiKeyField, SettingsCard, SettingsContainer, SettingsFormField, SettingsPageHeader, Spinner, StatCard, StatusAlert, SupportModal, TagInput, ThemeToggle, } from './ui-components';
|
package/index.js
CHANGED
|
@@ -31,6 +31,7 @@ export { customTheme } from './theme';
|
|
|
31
31
|
// Export components
|
|
32
32
|
// Export console components
|
|
33
33
|
export { buildGraphAwareConsoleConfig, ConsoleContent, EXAMPLE_SETS, getGraphExampleKind, useGraphAwareConsoleConfig, } from './components/console';
|
|
34
|
+
export { anchoredMessage, errorDetail, ReportChat, } from './components/report-chat';
|
|
34
35
|
export { ActiveSubscriptions, BrowseRepositories, EntitySelector, EntitySelectorCore, GraphSelectorCore, PageLayout, RepositoryGuard, SearchBar, SearchContent, SearchHitCard, SearchPagination, SearchResultsMeta, useIsRepository, } from './components';
|
|
35
36
|
// Export graph filters
|
|
36
37
|
export { composeFilters, excludeRepositories, excludeSubgraphs, GraphFilters, hasSchemaExtension, onlyRepositories, onlyUserGraphs, } from './components/graph-filters';
|
package/package.json
CHANGED