@trustgraph/react-state 0.3.1 → 1.1.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/dist/index.cjs CHANGED
@@ -6,6 +6,7 @@ var react = require('react');
6
6
  var zustand = require('zustand');
7
7
  var reactQuery = require('@tanstack/react-query');
8
8
  var uuid = require('uuid');
9
+ var similarity = require('compute-cosine-similarity');
9
10
 
10
11
  // Create context for notification handler
11
12
  const NotificationContext = react.createContext(null);
@@ -170,6 +171,16 @@ const useConversation = zustand.create()((set) => ({
170
171
  },
171
172
  ],
172
173
  })),
174
+ updateLastMessage: (text) => set((state) => {
175
+ if (state.messages.length === 0)
176
+ return state;
177
+ const messages = [...state.messages];
178
+ messages[messages.length - 1] = {
179
+ ...messages[messages.length - 1],
180
+ text: text,
181
+ };
182
+ return { messages };
183
+ }),
173
184
  setInput: (v) => set(() => ({
174
185
  input: v,
175
186
  })),
@@ -1409,273 +1420,6 @@ const useGraphEmbeddings = ({ flow, vecs, limit, collection }) => {
1409
1420
  };
1410
1421
  };
1411
1422
 
1412
- function getDefaultExportFromCjs (x) {
1413
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
1414
- }
1415
-
1416
- /**
1417
- * FUNCTION: isArray( value )
1418
- * Validates if a value is an array.
1419
- *
1420
- * @param {*} value - value to be validated
1421
- * @returns {Boolean} boolean indicating whether value is an array
1422
- */
1423
- function isArray$3( value ) {
1424
- return Object.prototype.toString.call( value ) === '[object Array]';
1425
- } // end FUNCTION isArray()
1426
-
1427
- // EXPORTS //
1428
-
1429
- var lib$4 = Array.isArray || isArray$3;
1430
-
1431
- /**
1432
- *
1433
- * VALIDATE: function
1434
- *
1435
- *
1436
- * DESCRIPTION:
1437
- * - Validates if a value is a function.
1438
- *
1439
- *
1440
- * NOTES:
1441
- * [1]
1442
- *
1443
- *
1444
- * TODO:
1445
- * [1]
1446
- *
1447
- *
1448
- * LICENSE:
1449
- * MIT
1450
- *
1451
- * Copyright (c) 2014. Athan Reines.
1452
- *
1453
- *
1454
- * AUTHOR:
1455
- * Athan Reines. kgryte@gmail.com. 2014.
1456
- *
1457
- */
1458
-
1459
- /**
1460
- * FUNCTION: isFunction( value )
1461
- * Validates if a value is a function.
1462
- *
1463
- * @param {*} value - value to be validated
1464
- * @returns {Boolean} boolean indicating whether value is a function
1465
- */
1466
- function isFunction$3( value ) {
1467
- return ( typeof value === 'function' );
1468
- } // end FUNCTION isFunction()
1469
-
1470
-
1471
- // EXPORTS //
1472
-
1473
- var lib$3 = isFunction$3;
1474
-
1475
- // MODULES //
1476
-
1477
- var isArray$2 = lib$4,
1478
- isFunction$2 = lib$3;
1479
-
1480
-
1481
- // DOT PRODUCT //
1482
-
1483
- /**
1484
- * FUNCTION: dot( x, y[, accessor] )
1485
- * Computes the dot product between two arrays.
1486
- *
1487
- * @param {Array} x - input array
1488
- * @param {Array} y - input array
1489
- * @param {Function} [accessor] - accessor function for accessing array values
1490
- * @returns {Number|Null} dot product
1491
- */
1492
- function dot$1( x, y, clbk ) {
1493
- if ( !isArray$2( x ) ) {
1494
- throw new TypeError( 'dot()::invalid input argument. First argument must be an array. Value: `' + x + '`.' );
1495
- }
1496
- if ( !isArray$2( y ) ) {
1497
- throw new TypeError( 'dot()::invalid input argument. Second argument must be an array. Value: `' + y + '`.' );
1498
- }
1499
- if ( arguments.length > 2 ) {
1500
- if ( !isFunction$2( clbk ) ) {
1501
- throw new TypeError( 'dot()::invalid input argument. Accessor must be a function. Value: `' + clbk + '`.' );
1502
- }
1503
- }
1504
- var len = x.length,
1505
- sum = 0,
1506
- i;
1507
-
1508
- if ( len !== y.length ) {
1509
- throw new Error( 'dot()::invalid input argument. Arrays must be of equal length.' );
1510
- }
1511
- if ( !len ) {
1512
- return null;
1513
- }
1514
- if ( clbk ) {
1515
- for ( i = 0; i < len; i++ ) {
1516
- sum += clbk( x[ i ], i, 0 ) * clbk( y[ i ], i, 1 );
1517
- }
1518
- } else {
1519
- for ( i = 0; i < len; i++ ) {
1520
- sum += x[ i ] * y[ i ];
1521
- }
1522
- }
1523
- return sum;
1524
- } // end FUNCTION dot()
1525
-
1526
-
1527
- // EXPORTS //
1528
-
1529
- var lib$2 = dot$1;
1530
-
1531
- // MODULES //
1532
-
1533
- var isArray$1 = lib$4,
1534
- isFunction$1 = lib$3;
1535
-
1536
-
1537
- // L2NORM //
1538
-
1539
- /**
1540
- * FUNCTION: l2norm( arr[, accessor] )
1541
- * Calculates the L2 norm (Euclidean norm) of an array.
1542
- *
1543
- * @param {Array} arr - input array
1544
- * @param {Function} [accessor] - accessor function for accessing array values
1545
- * @returns {Number|Null} L2 norm or null
1546
- */
1547
- function l2norm$1( arr, clbk ) {
1548
- if ( !isArray$1( arr ) ) {
1549
- throw new TypeError( 'l2norm()::invalid input argument. Must provide an array. Value: `' + arr + '`.' );
1550
- }
1551
- if ( arguments.length > 1 ) {
1552
- if ( !isFunction$1( clbk ) ) {
1553
- throw new TypeError( 'l2norm()::invalid input argument. Accessor must be a function. Value: `' + clbk + '`.' );
1554
- }
1555
- }
1556
- var len = arr.length,
1557
- t = 0,
1558
- s = 1,
1559
- r,
1560
- val,
1561
- abs,
1562
- i;
1563
-
1564
- if ( !len ) {
1565
- return null;
1566
- }
1567
- if ( clbk ) {
1568
- for ( i = 0; i < len; i++ ) {
1569
- val = clbk( arr[ i ], i );
1570
- abs = ( val < 0 ) ? -val : val;
1571
- if ( abs > 0 ) {
1572
- if ( abs > t ) {
1573
- r = t / val;
1574
- s = 1 + s*r*r;
1575
- t = abs;
1576
- } else {
1577
- r = val / t;
1578
- s = s + r*r;
1579
- }
1580
- }
1581
- }
1582
- } else {
1583
- for ( i = 0; i < len; i++ ) {
1584
- val = arr[ i ];
1585
- abs = ( val < 0 ) ? -val : val;
1586
- if ( abs > 0 ) {
1587
- if ( abs > t ) {
1588
- r = t / val;
1589
- s = 1 + s*r*r;
1590
- t = abs;
1591
- } else {
1592
- r = val / t;
1593
- s = s + r*r;
1594
- }
1595
- }
1596
- }
1597
- }
1598
- return t * Math.sqrt( s );
1599
- } // end FUNCTION l2norm()
1600
-
1601
-
1602
- // EXPORTS //
1603
-
1604
- var lib$1 = l2norm$1;
1605
-
1606
- // MODULES //
1607
-
1608
- var dot = lib$2,
1609
- l2norm = lib$1,
1610
- isArray = lib$4,
1611
- isFunction = lib$3;
1612
-
1613
-
1614
- // FUNCTIONS //
1615
-
1616
- /**
1617
- * Partially applied function from the right.
1618
- *
1619
- * @private
1620
- * @param {Function} fn - input function
1621
- * @param {number} j - array index
1622
- * @returns {Function} partially applied function
1623
- */
1624
- function partial( fn, j ) {
1625
- return function accessor( d, i ) {
1626
- return fn( d, i, j );
1627
- };
1628
- }
1629
-
1630
-
1631
- // MAIN //
1632
-
1633
- /**
1634
- * Computes the cosine similarity between two arrays.
1635
- *
1636
- * @param {number[]|Array} x - input array
1637
- * @param {number[]|Array} y - input array
1638
- * @param {Function} [accessor] - accessor function for accessing array values
1639
- * @returns {number|null} cosine similarity or null
1640
- */
1641
- function similarity( x, y, clbk ) {
1642
- var a, b, c;
1643
- if ( !isArray( x ) ) {
1644
- throw new TypeError( 'cosine-similarity()::invalid input argument. First argument must be an array. Value: `' + x + '`.' );
1645
- }
1646
- if ( !isArray( y ) ) {
1647
- throw new TypeError( 'cosine-similarity()::invalid input argument. Second argument must be an array. Value: `' + y + '`.' );
1648
- }
1649
- if ( arguments.length > 2 ) {
1650
- if ( !isFunction( clbk ) ) {
1651
- throw new TypeError( 'cosine-similarity()::invalid input argument. Accessor must be a function. Value: `' + clbk + '`.' );
1652
- }
1653
- }
1654
- if ( x.length !== y.length ) {
1655
- throw new Error( 'cosine-similarity()::invalid input argument. Input arrays must have the same length.' );
1656
- }
1657
- if ( !x.length ) {
1658
- return null;
1659
- }
1660
- if ( clbk ) {
1661
- a = dot( x, y, clbk );
1662
- b = l2norm( x, partial( clbk, 0 ) );
1663
- c = l2norm( y, partial( clbk, 1 ) );
1664
- } else {
1665
- a = dot( x, y );
1666
- b = l2norm( x );
1667
- c = l2norm( y );
1668
- }
1669
- return a / ( b*c );
1670
- }
1671
-
1672
-
1673
- // EXPORTS //
1674
-
1675
- var lib = similarity;
1676
-
1677
- var similarity$1 = /*@__PURE__*/getDefaultExportFromCjs(lib);
1678
-
1679
1423
  // Take the embeddings, and lookup entities using graph
1680
1424
  // embeddings, add embedding to each entity row, just an easy
1681
1425
  // place to put it
@@ -1795,7 +1539,7 @@ const addRowEmbeddings = (socket, add, remove) => (entities) => {
1795
1539
  };
1796
1540
  // Rest of the procecess is not async, so not adding progress
1797
1541
  const computeCosineSimilarity = () => (entities) => entities.map((ent) => {
1798
- const sim = similarity$1(ent.target, ent.embeddings);
1542
+ const sim = similarity(ent.target, ent.embeddings);
1799
1543
  return {
1800
1544
  uri: ent.uri,
1801
1545
  label: ent.label,
@@ -1965,11 +1709,27 @@ const useInference = () => {
1965
1709
  * Graph RAG inference with entity discovery
1966
1710
  */
1967
1711
  const graphRagMutation = reactQuery.useMutation({
1968
- mutationFn: async ({ input, options, collection, }) => {
1969
- // Execute Graph RAG request
1970
- const response = await socket
1971
- .flow(flowId)
1972
- .graphRag(input, options || {}, collection);
1712
+ mutationFn: async ({ input, options, collection, callbacks, }) => {
1713
+ // If callbacks provided, use streaming API
1714
+ const response = callbacks
1715
+ ? await new Promise((resolve, reject) => {
1716
+ let accumulated = "";
1717
+ const onChunk = (chunk, complete) => {
1718
+ accumulated += chunk;
1719
+ callbacks?.onChunk?.(chunk, complete);
1720
+ if (complete) {
1721
+ resolve(accumulated);
1722
+ }
1723
+ };
1724
+ const onError = (error) => {
1725
+ callbacks?.onError?.(error);
1726
+ reject(new Error(error));
1727
+ };
1728
+ socket
1729
+ .flow(flowId)
1730
+ .graphRagStreaming(input, onChunk, onError, options, collection);
1731
+ })
1732
+ : await socket.flow(flowId).graphRag(input, options || {}, collection);
1973
1733
  // Get embeddings for entity discovery
1974
1734
  const embeddings = await socket.flow(flowId).embeddings(input);
1975
1735
  // Query graph embeddings to find entities
@@ -1983,8 +1743,27 @@ const useInference = () => {
1983
1743
  * Basic LLM text completion
1984
1744
  */
1985
1745
  const textCompletionMutation = reactQuery.useMutation({
1986
- mutationFn: async ({ systemPrompt, input, }) => {
1987
- return await socket.flow(flowId).textCompletion(systemPrompt, input);
1746
+ mutationFn: async ({ systemPrompt, input, callbacks, }) => {
1747
+ // If callbacks provided, use streaming API
1748
+ return callbacks
1749
+ ? await new Promise((resolve, reject) => {
1750
+ let accumulated = "";
1751
+ const onChunk = (chunk, complete) => {
1752
+ accumulated += chunk;
1753
+ callbacks?.onChunk?.(chunk, complete);
1754
+ if (complete) {
1755
+ resolve(accumulated);
1756
+ }
1757
+ };
1758
+ const onError = (error) => {
1759
+ callbacks?.onError?.(error);
1760
+ reject(new Error(error));
1761
+ };
1762
+ socket
1763
+ .flow(flowId)
1764
+ .textCompletionStreaming(systemPrompt, input, onChunk, onError);
1765
+ })
1766
+ : await socket.flow(flowId).textCompletion(systemPrompt, input);
1988
1767
  },
1989
1768
  });
1990
1769
  /**
@@ -1993,15 +1772,19 @@ const useInference = () => {
1993
1772
  const agentMutation = reactQuery.useMutation({
1994
1773
  mutationFn: async ({ input, callbacks, }) => {
1995
1774
  return new Promise((resolve, reject) => {
1996
- const onThink = (thought) => {
1997
- callbacks?.onThink?.(thought);
1775
+ let fullAnswer = "";
1776
+ const onThink = (thought, complete) => {
1777
+ callbacks?.onThink?.(thought, complete);
1998
1778
  };
1999
- const onObserve = (observation) => {
2000
- callbacks?.onObserve?.(observation);
1779
+ const onObserve = (observation, complete) => {
1780
+ callbacks?.onObserve?.(observation, complete);
2001
1781
  };
2002
- const onAnswer = (answer) => {
2003
- callbacks?.onAnswer?.(answer);
2004
- resolve(answer);
1782
+ const onAnswer = (answer, complete) => {
1783
+ fullAnswer += answer;
1784
+ callbacks?.onAnswer?.(answer, complete);
1785
+ if (complete) {
1786
+ resolve(fullAnswer);
1787
+ }
2005
1788
  };
2006
1789
  const onError = (error) => {
2007
1790
  callbacks?.onError?.(error);
@@ -2033,6 +1816,7 @@ const useChatSession = () => {
2033
1816
  const notify = useNotification();
2034
1817
  // Conversation state
2035
1818
  const addMessage = useConversation((state) => state.addMessage);
1819
+ const updateLastMessage = useConversation((state) => state.updateLastMessage);
2036
1820
  const setInput = useConversation((state) => state.setInput);
2037
1821
  const chatMode = useConversation((state) => state.chatMode);
2038
1822
  // Progress and activity management
@@ -2052,8 +1836,10 @@ const useChatSession = () => {
2052
1836
  const ragActivity = "Graph RAG: " + input;
2053
1837
  const embActivity = "Find entities: " + input;
2054
1838
  addActivity(ragActivity);
1839
+ let accumulated = "";
1840
+ let messageAdded = false;
2055
1841
  try {
2056
- // Execute Graph RAG with entity discovery
1842
+ // Execute Graph RAG with streaming and entity discovery
2057
1843
  const result = await inference.graphRag({
2058
1844
  input,
2059
1845
  options: {
@@ -2063,8 +1849,21 @@ const useChatSession = () => {
2063
1849
  pathLength: settings.graphrag.pathLength,
2064
1850
  },
2065
1851
  collection: settings.collection,
1852
+ callbacks: {
1853
+ onChunk: (chunk, complete) => {
1854
+ accumulated += chunk;
1855
+ if (!messageAdded) {
1856
+ // Add empty message on first chunk
1857
+ addMessage("ai", accumulated);
1858
+ messageAdded = true;
1859
+ }
1860
+ else {
1861
+ // Update existing message with accumulated text
1862
+ updateLastMessage(accumulated);
1863
+ }
1864
+ },
1865
+ },
2066
1866
  });
2067
- addMessage("ai", result.response);
2068
1867
  removeActivity(ragActivity);
2069
1868
  // Start embeddings activity
2070
1869
  addActivity(embActivity);
@@ -2108,12 +1907,27 @@ const useChatSession = () => {
2108
1907
  const handleBasicLlm = async (input) => {
2109
1908
  const activity = "Text completion: " + input;
2110
1909
  addActivity(activity);
1910
+ let accumulated = "";
1911
+ let messageAdded = false;
2111
1912
  try {
2112
1913
  const response = await inference.textCompletion({
2113
1914
  systemPrompt: "You are a helpful assistant. Provide clear and concise responses.",
2114
1915
  input,
1916
+ callbacks: {
1917
+ onChunk: (chunk, complete) => {
1918
+ accumulated += chunk;
1919
+ if (!messageAdded) {
1920
+ // Add empty message on first chunk
1921
+ addMessage("ai", accumulated);
1922
+ messageAdded = true;
1923
+ }
1924
+ else {
1925
+ // Update existing message with accumulated text
1926
+ updateLastMessage(accumulated);
1927
+ }
1928
+ },
1929
+ },
2115
1930
  });
2116
- addMessage("ai", response);
2117
1931
  removeActivity(activity);
2118
1932
  setEntities([]);
2119
1933
  return response;
@@ -2129,13 +1943,54 @@ const useChatSession = () => {
2129
1943
  const handleAgent = async (input) => {
2130
1944
  const activity = "Agent: " + input;
2131
1945
  addActivity(activity);
1946
+ let thinkingAccumulated = "";
1947
+ let thinkingMessageAdded = false;
1948
+ let observationAccumulated = "";
1949
+ let observationMessageAdded = false;
1950
+ let answerAccumulated = "";
1951
+ let answerMessageAdded = false;
2132
1952
  try {
2133
1953
  const response = await inference.agent({
2134
1954
  input,
2135
1955
  callbacks: {
2136
- onThink: (thought) => addMessage("ai", thought, "thinking"),
2137
- onObserve: (observation) => addMessage("ai", observation, "observation"),
2138
- onAnswer: (answer) => addMessage("ai", answer, "answer"),
1956
+ onThink: (thought, complete) => {
1957
+ thinkingAccumulated += thought;
1958
+ if (!thinkingMessageAdded) {
1959
+ addMessage("ai", thinkingAccumulated, "thinking");
1960
+ thinkingMessageAdded = true;
1961
+ }
1962
+ else {
1963
+ updateLastMessage(thinkingAccumulated);
1964
+ }
1965
+ if (complete) {
1966
+ thinkingAccumulated = "";
1967
+ thinkingMessageAdded = false;
1968
+ }
1969
+ },
1970
+ onObserve: (observation, complete) => {
1971
+ observationAccumulated += observation;
1972
+ if (!observationMessageAdded) {
1973
+ addMessage("ai", observationAccumulated, "observation");
1974
+ observationMessageAdded = true;
1975
+ }
1976
+ else {
1977
+ updateLastMessage(observationAccumulated);
1978
+ }
1979
+ if (complete) {
1980
+ observationAccumulated = "";
1981
+ observationMessageAdded = false;
1982
+ }
1983
+ },
1984
+ onAnswer: (answer, complete) => {
1985
+ answerAccumulated += answer;
1986
+ if (!answerMessageAdded) {
1987
+ addMessage("ai", answerAccumulated, "answer");
1988
+ answerMessageAdded = true;
1989
+ }
1990
+ else {
1991
+ updateLastMessage(answerAccumulated);
1992
+ }
1993
+ },
2139
1994
  },
2140
1995
  });
2141
1996
  removeActivity(activity);