@coral-ai/chat 0.4.1 → 0.4.2

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/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
3
  import { ChatFontSize, ChatFontFamily } from '@coral-ai/markdown';
4
+ import { CoralChartSpec } from '@coral-ai/charts';
4
5
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
6
 
6
7
  type ErrorCategory = "auth_billing" | "rate_limit" | "overloaded" | "bad_input" | "not_found" | "server_error" | "unknown";
@@ -47,6 +48,14 @@ type ChatMessage = {
47
48
  * persists in history for replay. Updated in place as the agent streams.
48
49
  */
49
50
  todos?: ChatTodo[];
51
+ /**
52
+ * If present, this assistant message carries one or more chart specs.
53
+ * Rendered inline (above the message text) via `<CoralChart>` from
54
+ * `@coral-ai/charts`. Streamed in incrementally as the agent calls the
55
+ * built-in `render_chart` tool, and persisted with the conversation so
56
+ * the visualizations survive reloads.
57
+ */
58
+ charts?: CoralChartSpec[];
50
59
  /**
51
60
  * If present, the turn that produced this assistant message failed. The
52
61
  * message renders as an error card (with a Retry button) instead of normal
@@ -116,6 +125,11 @@ type ChatChunk = string | {
116
125
  } | {
117
126
  type: "todos";
118
127
  todos: ChatTodo[];
128
+ }
129
+ /** Appends a chart spec to the current assistant message; renders inline. */
130
+ | {
131
+ type: "chart";
132
+ spec: CoralChartSpec;
119
133
  };
120
134
  type ChatResponse = string | AsyncIterable<ChatChunk> | {
121
135
  content: string;
@@ -478,6 +492,8 @@ type AgentSessionStore = {
478
492
  declare const UPDATE_TODOS_TOOL: ToolSpec;
479
493
  /** Built-in tool: pause and ask the user to approve / choose before continuing. */
480
494
  declare const REQUEST_APPROVAL_TOOL: ToolSpec;
495
+ /** Built-in tool: render a chart inline as part of the assistant message. */
496
+ declare const RENDER_CHART_TOOL: ToolSpec;
481
497
  type CreateAgentHandlerOptions = {
482
498
  /** Provider adapter — the only provider-specific code. */
483
499
  callModel: CallModel;
@@ -658,4 +674,4 @@ declare const Chat: typeof Chat$1 & {
658
674
  declare const normalizeChatError: typeof normalizeChatError$1;
659
675
  declare const createHandler: typeof createHandler$1;
660
676
 
661
- export { type AgentHandler, type AgentMessage, type AgentSessionStore, type AgentSnapshot, type AgentTool, type ApprovalOption, type CallModel, Chat, type ChatApprovalProps, type ChatApprovalRequest, type ChatAttachButtonProps, type ChatAttachType, type ChatAttachment, type ChatCanvasProps, type ChatChunk, type ChatComponents, type ChatErrorProps, type ChatInputProps, type ChatMessage, type ChatMessageRendererProps, type ChatMessagesProps, type ChatResponse, type ChatThread, type ChatThreadsAdapter, type ChatThreadsContextValue, ChatThreadsProvider, type ChatThreadsProviderProps, type ChatTodo, type ChatTodoListProps, type CoralTaskType, type CreateAgentHandlerOptions, DEFAULT_TASK_MESSAGES, type ErrorCategory, type ModelRequest, type ModelStreamEvent, type NormalizedChatError, REQUEST_APPROVAL_TOOL, type ToolCall, type ToolResult, type ToolSpec, UPDATE_TODOS_TOOL, createAgentHandler, createHandler, createMemoryThreadsAdapter, fileToBase64, normalizeChatError, resolveTaskMessages, useChatContext, useChatThreads };
677
+ export { type AgentHandler, type AgentMessage, type AgentSessionStore, type AgentSnapshot, type AgentTool, type ApprovalOption, type CallModel, Chat, type ChatApprovalProps, type ChatApprovalRequest, type ChatAttachButtonProps, type ChatAttachType, type ChatAttachment, type ChatCanvasProps, type ChatChunk, type ChatComponents, type ChatErrorProps, type ChatInputProps, type ChatMessage, type ChatMessageRendererProps, type ChatMessagesProps, type ChatResponse, type ChatThread, type ChatThreadsAdapter, type ChatThreadsContextValue, ChatThreadsProvider, type ChatThreadsProviderProps, type ChatTodo, type ChatTodoListProps, type CoralTaskType, type CreateAgentHandlerOptions, DEFAULT_TASK_MESSAGES, type ErrorCategory, type ModelRequest, type ModelStreamEvent, type NormalizedChatError, RENDER_CHART_TOOL, REQUEST_APPROVAL_TOOL, type ToolCall, type ToolResult, type ToolSpec, UPDATE_TODOS_TOOL, createAgentHandler, createHandler, createMemoryThreadsAdapter, fileToBase64, normalizeChatError, resolveTaskMessages, useChatContext, useChatThreads };
package/dist/index.js CHANGED
@@ -428,6 +428,7 @@ var ChatCanvas = ({
428
428
  let pendingContent = "";
429
429
  let streamedApproval;
430
430
  let streamedTodos;
431
+ let streamedCharts;
431
432
  for await (const chunk of response) {
432
433
  if (shouldStopRef.current) break;
433
434
  if (sessionAtSend !== void 0 && currentConversationIdRef.current !== sessionAtSend) break;
@@ -476,6 +477,17 @@ var ChatCanvas = ({
476
477
  }
477
478
  return updated;
478
479
  });
480
+ } else if (chunk.type === "chart") {
481
+ streamedCharts = [...streamedCharts ?? [], chunk.spec];
482
+ const nextCharts = streamedCharts;
483
+ setMessages((prev) => {
484
+ const updated = [...prev];
485
+ const last = updated[updated.length - 1];
486
+ if (last && last.id === assistantId) {
487
+ updated[updated.length - 1] = { ...last, charts: nextCharts };
488
+ }
489
+ return updated;
490
+ });
479
491
  }
480
492
  }
481
493
  if (rafId !== null) cancelAnimationFrame(rafId);
@@ -489,7 +501,8 @@ var ChatCanvas = ({
489
501
  content: assistantContent,
490
502
  ...reasoningRef.current ? { metadata: { reasoning: reasoningRef.current } } : {},
491
503
  ...streamedApproval ? { approval: streamedApproval } : {},
492
- ...streamedTodos ? { todos: streamedTodos } : {}
504
+ ...streamedTodos ? { todos: streamedTodos } : {},
505
+ ...streamedCharts ? { charts: streamedCharts } : {}
493
506
  };
494
507
  const finalMessages = [...updatedMessages, finalMsg];
495
508
  setMessages(finalMessages);
@@ -1041,13 +1054,13 @@ import {
1041
1054
  Body1 as Body14,
1042
1055
  Caption1,
1043
1056
  makeStyles as makeStyles6,
1044
- Spinner as Spinner2,
1045
1057
  tokens as tokens6
1046
1058
  } from "@fluentui/react-components";
1047
1059
  import {
1048
- CheckmarkCircle16Filled,
1049
- ChevronRight16Regular as ChevronRight16Regular2,
1050
- Circle16Regular
1060
+ CheckboxCheckedFilled,
1061
+ CheckboxUncheckedRegular,
1062
+ ChevronRightRegular,
1063
+ SquareHintRegular
1051
1064
  } from "@fluentui/react-icons";
1052
1065
  import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1053
1066
  var useStyles6 = makeStyles6({
@@ -1084,6 +1097,7 @@ var useStyles6 = makeStyles6({
1084
1097
  chevron: {
1085
1098
  color: tokens6.colorNeutralForeground3,
1086
1099
  flexShrink: 0,
1100
+ fontSize: "16px",
1087
1101
  transition: "transform 0.2s ease"
1088
1102
  },
1089
1103
  chevronExpanded: {
@@ -1104,15 +1118,19 @@ var useStyles6 = makeStyles6({
1104
1118
  display: "flex",
1105
1119
  alignItems: "center",
1106
1120
  justifyContent: "center",
1107
- width: "16px",
1121
+ width: "20px",
1108
1122
  height: "20px",
1123
+ fontSize: "20px",
1109
1124
  flexShrink: 0
1110
1125
  },
1111
1126
  pendingIcon: {
1112
- color: tokens6.colorNeutralForeground4
1127
+ color: tokens6.colorNeutralForegroundDisabled
1128
+ },
1129
+ activeIcon: {
1130
+ color: tokens6.colorNeutralForeground2
1113
1131
  },
1114
1132
  doneIcon: {
1115
- color: tokens6.colorStatusSuccessForeground1
1133
+ color: tokens6.colorPaletteSeafoamForeground2
1116
1134
  },
1117
1135
  text: {
1118
1136
  minWidth: 0,
@@ -1157,7 +1175,7 @@ var ChatTodoList = ({
1157
1175
  "aria-expanded": expanded,
1158
1176
  children: [
1159
1177
  /* @__PURE__ */ jsx6(
1160
- ChevronRight16Regular2,
1178
+ ChevronRightRegular,
1161
1179
  {
1162
1180
  className: `${styles.chevron}${expanded ? ` ${styles.chevronExpanded}` : ""}`
1163
1181
  }
@@ -1180,7 +1198,7 @@ var ChatTodoList = ({
1180
1198
  className: styles.item,
1181
1199
  role: "listitem",
1182
1200
  children: [
1183
- /* @__PURE__ */ jsx6("span", { className: styles.iconCell, children: todo.status === "completed" ? /* @__PURE__ */ jsx6(CheckmarkCircle16Filled, { className: styles.doneIcon }) : todo.status === "in_progress" ? /* @__PURE__ */ jsx6(Spinner2, { size: "extra-tiny" }) : /* @__PURE__ */ jsx6(Circle16Regular, { className: styles.pendingIcon }) }),
1201
+ /* @__PURE__ */ jsx6("span", { className: styles.iconCell, children: todo.status === "completed" ? /* @__PURE__ */ jsx6(CheckboxCheckedFilled, { className: styles.doneIcon }) : todo.status === "in_progress" ? /* @__PURE__ */ jsx6(CheckboxUncheckedRegular, { className: styles.activeIcon }) : /* @__PURE__ */ jsx6(SquareHintRegular, { className: styles.pendingIcon }) }),
1184
1202
  /* @__PURE__ */ jsx6(Body14, { className: `${styles.text} ${textClass}`, children: text })
1185
1203
  ]
1186
1204
  },
@@ -1194,6 +1212,7 @@ var ChatTodoList = ({
1194
1212
  var Chat_TodoList_default = ChatTodoList;
1195
1213
 
1196
1214
  // src/Chat.Messages.tsx
1215
+ import { CoralChart } from "@coral-ai/charts";
1197
1216
  import { CheckmarkCircle20Filled } from "@fluentui/react-icons";
1198
1217
  import { Fragment, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1199
1218
  var useStyles7 = makeStyles7({
@@ -1560,6 +1579,7 @@ var ChatMessages = ({
1560
1579
  }
1561
1580
  ),
1562
1581
  msg.todos && msg.todos.length > 0 && /* @__PURE__ */ jsx7(Chat_TodoList_default, { todos: msg.todos }),
1582
+ msg.charts && msg.charts.length > 0 && /* @__PURE__ */ jsx7(Fragment, { children: msg.charts.map((spec, i) => /* @__PURE__ */ jsx7(CoralChart, { spec }, i)) }),
1563
1583
  /* @__PURE__ */ jsx7(
1564
1584
  CoralMarkdown,
1565
1585
  {
@@ -2457,6 +2477,49 @@ var REQUEST_APPROVAL_TOOL = {
2457
2477
  required: ["question", "options"]
2458
2478
  }
2459
2479
  };
2480
+ var RENDER_CHART_TOOL = {
2481
+ name: "render_chart",
2482
+ description: 'Render a chart inline in the assistant message. Call this when a visualization (bar or line) communicates the data more clearly than prose or a table. The chart appears above your text \u2014 refer to it naturally ("as the chart above shows\u2026") and do NOT also produce ASCII charts or code blocks for the same data. May be called multiple times in a turn to render multiple charts.',
2483
+ parameters: {
2484
+ type: "object",
2485
+ properties: {
2486
+ kind: {
2487
+ type: "string",
2488
+ enum: ["bar", "line"],
2489
+ description: "Use 'bar' for categorical comparisons, 'line' for trends over an ordered axis."
2490
+ },
2491
+ title: { type: "string", description: "Title shown above the chart." },
2492
+ xKey: {
2493
+ type: "string",
2494
+ description: "Key in each `data` row that supplies the x-axis label."
2495
+ },
2496
+ series: {
2497
+ type: "array",
2498
+ description: "One entry per data series (one bar group / one line).",
2499
+ items: {
2500
+ type: "object",
2501
+ properties: {
2502
+ dataKey: {
2503
+ type: "string",
2504
+ description: "Key in each `data` row holding this series' numeric value."
2505
+ },
2506
+ label: {
2507
+ type: "string",
2508
+ description: "Display label shown in the legend and tooltip."
2509
+ }
2510
+ },
2511
+ required: ["dataKey", "label"]
2512
+ }
2513
+ },
2514
+ data: {
2515
+ type: "array",
2516
+ description: "Rows of data containing the xKey and every series' dataKey. Example: `[{ quarter: 'Q1', alice: 92, bob: 78 }, \u2026]`.",
2517
+ items: { type: "object" }
2518
+ }
2519
+ },
2520
+ required: ["kind", "title", "xKey", "series", "data"]
2521
+ }
2522
+ };
2460
2523
  function createMemoryStore() {
2461
2524
  const map = /* @__PURE__ */ new Map();
2462
2525
  return {
@@ -2490,6 +2553,24 @@ function extractTodos(input) {
2490
2553
  };
2491
2554
  });
2492
2555
  }
2556
+ function buildChartSpec(input) {
2557
+ const o = input ?? {};
2558
+ const kind = o.kind === "line" ? "line" : "bar";
2559
+ const title = typeof o.title === "string" ? o.title : "";
2560
+ const xKey = typeof o.xKey === "string" ? o.xKey : "";
2561
+ const rawSeries = Array.isArray(o.series) ? o.series : [];
2562
+ const series = rawSeries.map((s, i) => {
2563
+ const obj = s ?? {};
2564
+ const dataKey = typeof obj.dataKey === "string" ? obj.dataKey : "";
2565
+ const label = typeof obj.label === "string" ? obj.label : dataKey || `series-${i}`;
2566
+ return dataKey ? { dataKey, label } : null;
2567
+ }).filter((s) => s !== null);
2568
+ const data = Array.isArray(o.data) ? o.data.filter(
2569
+ (r) => r && typeof r === "object"
2570
+ ) : [];
2571
+ if (!xKey || series.length === 0 || data.length === 0) return null;
2572
+ return { kind, title, xKey, series, data };
2573
+ }
2493
2574
  function buildApproval(tc) {
2494
2575
  const input = tc.input ?? {};
2495
2576
  const rawOptions = Array.isArray(input.options) ? input.options : [];
@@ -2518,6 +2599,7 @@ function createAgentHandler(options) {
2518
2599
  const toolSpecs = [
2519
2600
  UPDATE_TODOS_TOOL,
2520
2601
  REQUEST_APPROVAL_TOOL,
2602
+ RENDER_CHART_TOOL,
2521
2603
  ...tools.map((t) => t.spec)
2522
2604
  ];
2523
2605
  return async function* (_input, history, attachments) {
@@ -2585,6 +2667,22 @@ function createAgentHandler(options) {
2585
2667
  yield { type: "approval", approval: buildApproval(tc) };
2586
2668
  await store.set(tc.id, { messages, pendingToolCallId: tc.id });
2587
2669
  return;
2670
+ } else if (tc.name === RENDER_CHART_TOOL.name) {
2671
+ const spec = buildChartSpec(tc.input);
2672
+ if (spec) {
2673
+ yield { type: "chart", spec };
2674
+ messages.push({
2675
+ role: "tool",
2676
+ toolCallId: tc.id,
2677
+ content: "Chart rendered and shown to the user above your text."
2678
+ });
2679
+ } else {
2680
+ messages.push({
2681
+ role: "tool",
2682
+ toolCallId: tc.id,
2683
+ content: "Error: chart spec was invalid. Need non-empty `xKey`, at least one entry in `series`, and at least one row in `data`."
2684
+ });
2685
+ }
2588
2686
  } else {
2589
2687
  const tool = customByName.get(tc.name);
2590
2688
  let content;
@@ -2781,6 +2879,7 @@ export {
2781
2879
  Chat2 as Chat,
2782
2880
  ChatThreadsProvider,
2783
2881
  DEFAULT_TASK_MESSAGES,
2882
+ RENDER_CHART_TOOL,
2784
2883
  REQUEST_APPROVAL_TOOL,
2785
2884
  UPDATE_TODOS_TOOL,
2786
2885
  createAgentHandler,