graphlit-client 1.0.20250612009 → 1.0.20250613002

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/client.d.ts CHANGED
@@ -86,6 +86,7 @@ declare class Graphlit {
86
86
  publishContents(publishPrompt: string, connector: Types.ContentPublishingConnectorInput, summaryPrompt?: string, summarySpecification?: Types.EntityReferenceInput, publishSpecification?: Types.EntityReferenceInput, name?: string, filter?: Types.ContentFilter, workflow?: Types.EntityReferenceInput, isSynchronous?: boolean, includeDetails?: boolean, correlationId?: string): Promise<Types.PublishContentsMutation>;
87
87
  publishText(text: string, textType: Types.TextTypes, connector: Types.ContentPublishingConnectorInput, name?: string, workflow?: Types.EntityReferenceInput, isSynchronous?: boolean, correlationId?: string): Promise<Types.PublishTextMutation>;
88
88
  getContent(id: string): Promise<Types.GetContentQuery>;
89
+ lookupContents(ids: string[]): Promise<Types.LookupContentsResults>;
89
90
  queryContents(filter?: Types.ContentFilter): Promise<Types.QueryContentsQuery>;
90
91
  queryContentsFacets(filter?: Types.ContentFilter): Promise<Types.QueryContentsFacetsQuery>;
91
92
  queryContentsGraph(filter?: Types.ContentFilter): Promise<Types.QueryContentsGraphQuery>;
package/dist/client.js CHANGED
@@ -489,6 +489,9 @@ class Graphlit {
489
489
  async getContent(id) {
490
490
  return this.queryAndCheckError(Documents.GetContent, { id: id });
491
491
  }
492
+ async lookupContents(ids) {
493
+ return this.queryAndCheckError(Documents.LookupContents, { ids: ids });
494
+ }
492
495
  async queryContents(filter) {
493
496
  return this.queryAndCheckError(Documents.QueryContents, { filter: filter });
494
497
  }
@@ -1980,14 +1983,21 @@ class Graphlit {
1980
1983
  }
1981
1984
  currentRound++;
1982
1985
  }
1983
- // Complete the conversation
1986
+ // Complete the conversation and get token count
1987
+ let finalTokens;
1984
1988
  if (fullMessage) {
1985
- await this.completeConversation(fullMessage.trim(), conversationId, correlationId);
1989
+ const completeResponse = await this.completeConversation(fullMessage.trim(), conversationId, correlationId);
1990
+ // Extract token count from the response
1991
+ finalTokens = completeResponse.completeConversation?.message?.tokens ?? undefined;
1992
+ if (process.env.DEBUG_GRAPHLIT_SDK_STREAMING) {
1993
+ console.log(`📊 [completeConversation] Tokens used: ${finalTokens || 'unknown'}`);
1994
+ }
1986
1995
  }
1987
- // Emit completion event
1996
+ // Emit completion event with token count
1988
1997
  uiAdapter.handleEvent({
1989
1998
  type: "complete",
1990
1999
  conversationId,
2000
+ tokens: finalTokens,
1991
2001
  });
1992
2002
  }
1993
2003
  /**
@@ -44,6 +44,7 @@ export declare const IngestText: import("graphql").DocumentNode;
44
44
  export declare const IngestTextBatch: import("graphql").DocumentNode;
45
45
  export declare const IngestUri: import("graphql").DocumentNode;
46
46
  export declare const IsContentDone: import("graphql").DocumentNode;
47
+ export declare const LookupContents: import("graphql").DocumentNode;
47
48
  export declare const PublishContents: import("graphql").DocumentNode;
48
49
  export declare const PublishText: import("graphql").DocumentNode;
49
50
  export declare const QueryContents: import("graphql").DocumentNode;
@@ -1583,6 +1583,293 @@ export const IsContentDone = gql `
1583
1583
  }
1584
1584
  }
1585
1585
  `;
1586
+ export const LookupContents = gql `
1587
+ query LookupContents($ids: [ID!]!, $correlationId: String) {
1588
+ lookupContents(ids: $ids, correlationId: $correlationId) {
1589
+ results {
1590
+ id
1591
+ name
1592
+ creationDate
1593
+ owner {
1594
+ id
1595
+ }
1596
+ state
1597
+ originalDate
1598
+ finishedDate
1599
+ fileCreationDate
1600
+ fileModifiedDate
1601
+ workflowDuration
1602
+ uri
1603
+ description
1604
+ identifier
1605
+ markdown
1606
+ address {
1607
+ streetAddress
1608
+ city
1609
+ region
1610
+ country
1611
+ postalCode
1612
+ }
1613
+ location {
1614
+ latitude
1615
+ longitude
1616
+ }
1617
+ h3 {
1618
+ h3r0
1619
+ h3r1
1620
+ h3r2
1621
+ h3r3
1622
+ h3r4
1623
+ h3r5
1624
+ h3r6
1625
+ h3r7
1626
+ h3r8
1627
+ h3r9
1628
+ h3r10
1629
+ h3r11
1630
+ h3r12
1631
+ h3r13
1632
+ h3r14
1633
+ h3r15
1634
+ }
1635
+ boundary
1636
+ epsgCode
1637
+ path
1638
+ features
1639
+ c4id
1640
+ type
1641
+ fileType
1642
+ mimeType
1643
+ format
1644
+ formatName
1645
+ fileName
1646
+ fileSize
1647
+ masterUri
1648
+ imageUri
1649
+ textUri
1650
+ audioUri
1651
+ transcriptUri
1652
+ summary
1653
+ customSummary
1654
+ keywords
1655
+ bullets
1656
+ headlines
1657
+ posts
1658
+ chapters
1659
+ questions
1660
+ video {
1661
+ width
1662
+ height
1663
+ duration
1664
+ make
1665
+ model
1666
+ software
1667
+ title
1668
+ description
1669
+ keywords
1670
+ author
1671
+ }
1672
+ audio {
1673
+ keywords
1674
+ author
1675
+ series
1676
+ episode
1677
+ episodeType
1678
+ season
1679
+ publisher
1680
+ copyright
1681
+ genre
1682
+ title
1683
+ description
1684
+ bitrate
1685
+ channels
1686
+ sampleRate
1687
+ bitsPerSample
1688
+ duration
1689
+ }
1690
+ image {
1691
+ width
1692
+ height
1693
+ resolutionX
1694
+ resolutionY
1695
+ bitsPerComponent
1696
+ components
1697
+ projectionType
1698
+ orientation
1699
+ description
1700
+ make
1701
+ model
1702
+ software
1703
+ lens
1704
+ focalLength
1705
+ exposureTime
1706
+ fNumber
1707
+ iso
1708
+ heading
1709
+ pitch
1710
+ }
1711
+ document {
1712
+ title
1713
+ subject
1714
+ summary
1715
+ author
1716
+ publisher
1717
+ description
1718
+ keywords
1719
+ pageCount
1720
+ worksheetCount
1721
+ slideCount
1722
+ wordCount
1723
+ lineCount
1724
+ paragraphCount
1725
+ isEncrypted
1726
+ hasDigitalSignature
1727
+ }
1728
+ email {
1729
+ identifier
1730
+ threadIdentifier
1731
+ subject
1732
+ labels
1733
+ sensitivity
1734
+ priority
1735
+ importance
1736
+ from {
1737
+ name
1738
+ email
1739
+ givenName
1740
+ familyName
1741
+ }
1742
+ to {
1743
+ name
1744
+ email
1745
+ givenName
1746
+ familyName
1747
+ }
1748
+ cc {
1749
+ name
1750
+ email
1751
+ givenName
1752
+ familyName
1753
+ }
1754
+ bcc {
1755
+ name
1756
+ email
1757
+ givenName
1758
+ familyName
1759
+ }
1760
+ }
1761
+ issue {
1762
+ identifier
1763
+ title
1764
+ project
1765
+ team
1766
+ status
1767
+ priority
1768
+ type
1769
+ labels
1770
+ }
1771
+ package {
1772
+ fileCount
1773
+ folderCount
1774
+ isEncrypted
1775
+ }
1776
+ language {
1777
+ languages
1778
+ }
1779
+ parent {
1780
+ id
1781
+ name
1782
+ }
1783
+ children {
1784
+ id
1785
+ name
1786
+ }
1787
+ feed {
1788
+ id
1789
+ name
1790
+ }
1791
+ collections {
1792
+ id
1793
+ name
1794
+ }
1795
+ links {
1796
+ uri
1797
+ linkType
1798
+ }
1799
+ observations {
1800
+ id
1801
+ type
1802
+ observable {
1803
+ id
1804
+ name
1805
+ }
1806
+ related {
1807
+ id
1808
+ name
1809
+ }
1810
+ relatedType
1811
+ relation
1812
+ occurrences {
1813
+ type
1814
+ confidence
1815
+ startTime
1816
+ endTime
1817
+ pageIndex
1818
+ boundingBox {
1819
+ left
1820
+ top
1821
+ width
1822
+ height
1823
+ }
1824
+ }
1825
+ state
1826
+ }
1827
+ workflow {
1828
+ id
1829
+ name
1830
+ }
1831
+ pages {
1832
+ index
1833
+ text
1834
+ relevance
1835
+ images {
1836
+ id
1837
+ mimeType
1838
+ data
1839
+ left
1840
+ right
1841
+ top
1842
+ bottom
1843
+ }
1844
+ chunks {
1845
+ index
1846
+ pageIndex
1847
+ rowIndex
1848
+ columnIndex
1849
+ confidence
1850
+ text
1851
+ role
1852
+ language
1853
+ relevance
1854
+ }
1855
+ }
1856
+ segments {
1857
+ startTime
1858
+ endTime
1859
+ text
1860
+ relevance
1861
+ }
1862
+ frames {
1863
+ index
1864
+ description
1865
+ text
1866
+ relevance
1867
+ }
1868
+ error
1869
+ }
1870
+ }
1871
+ }
1872
+ `;
1586
1873
  export const PublishContents = gql `
1587
1874
  mutation PublishContents($summaryPrompt: String, $publishPrompt: String!, $connector: ContentPublishingConnectorInput!, $filter: ContentFilter, $includeDetails: Boolean, $isSynchronous: Boolean, $correlationId: String, $name: String, $summarySpecification: EntityReferenceInput, $publishSpecification: EntityReferenceInput, $workflow: EntityReferenceInput) {
1588
1875
  publishContents(
@@ -15784,6 +15784,330 @@ export type IsContentDoneQuery = {
15784
15784
  result?: boolean | null;
15785
15785
  } | null;
15786
15786
  };
15787
+ export type LookupContentsQueryVariables = Exact<{
15788
+ ids: Array<Scalars['ID']['input']> | Scalars['ID']['input'];
15789
+ correlationId?: InputMaybe<Scalars['String']['input']>;
15790
+ }>;
15791
+ export type LookupContentsQuery = {
15792
+ __typename?: 'Query';
15793
+ lookupContents?: {
15794
+ __typename?: 'LookupContentsResults';
15795
+ results?: Array<{
15796
+ __typename?: 'Content';
15797
+ id: string;
15798
+ name: string;
15799
+ creationDate: any;
15800
+ state: EntityState;
15801
+ originalDate?: any | null;
15802
+ finishedDate?: any | null;
15803
+ fileCreationDate?: any | null;
15804
+ fileModifiedDate?: any | null;
15805
+ workflowDuration?: any | null;
15806
+ uri?: any | null;
15807
+ description?: string | null;
15808
+ identifier?: string | null;
15809
+ markdown?: string | null;
15810
+ boundary?: string | null;
15811
+ epsgCode?: number | null;
15812
+ path?: string | null;
15813
+ features?: string | null;
15814
+ c4id?: string | null;
15815
+ type?: ContentTypes | null;
15816
+ fileType?: FileTypes | null;
15817
+ mimeType?: string | null;
15818
+ format?: string | null;
15819
+ formatName?: string | null;
15820
+ fileName?: string | null;
15821
+ fileSize?: any | null;
15822
+ masterUri?: any | null;
15823
+ imageUri?: any | null;
15824
+ textUri?: any | null;
15825
+ audioUri?: any | null;
15826
+ transcriptUri?: any | null;
15827
+ summary?: string | null;
15828
+ customSummary?: string | null;
15829
+ keywords?: Array<string> | null;
15830
+ bullets?: Array<string> | null;
15831
+ headlines?: Array<string> | null;
15832
+ posts?: Array<string> | null;
15833
+ chapters?: Array<string> | null;
15834
+ questions?: Array<string> | null;
15835
+ error?: string | null;
15836
+ owner: {
15837
+ __typename?: 'Owner';
15838
+ id: string;
15839
+ };
15840
+ address?: {
15841
+ __typename?: 'Address';
15842
+ streetAddress?: string | null;
15843
+ city?: string | null;
15844
+ region?: string | null;
15845
+ country?: string | null;
15846
+ postalCode?: string | null;
15847
+ } | null;
15848
+ location?: {
15849
+ __typename?: 'Point';
15850
+ latitude?: number | null;
15851
+ longitude?: number | null;
15852
+ } | null;
15853
+ h3?: {
15854
+ __typename?: 'H3';
15855
+ h3r0?: string | null;
15856
+ h3r1?: string | null;
15857
+ h3r2?: string | null;
15858
+ h3r3?: string | null;
15859
+ h3r4?: string | null;
15860
+ h3r5?: string | null;
15861
+ h3r6?: string | null;
15862
+ h3r7?: string | null;
15863
+ h3r8?: string | null;
15864
+ h3r9?: string | null;
15865
+ h3r10?: string | null;
15866
+ h3r11?: string | null;
15867
+ h3r12?: string | null;
15868
+ h3r13?: string | null;
15869
+ h3r14?: string | null;
15870
+ h3r15?: string | null;
15871
+ } | null;
15872
+ video?: {
15873
+ __typename?: 'VideoMetadata';
15874
+ width?: number | null;
15875
+ height?: number | null;
15876
+ duration?: any | null;
15877
+ make?: string | null;
15878
+ model?: string | null;
15879
+ software?: string | null;
15880
+ title?: string | null;
15881
+ description?: string | null;
15882
+ keywords?: Array<string | null> | null;
15883
+ author?: string | null;
15884
+ } | null;
15885
+ audio?: {
15886
+ __typename?: 'AudioMetadata';
15887
+ keywords?: Array<string | null> | null;
15888
+ author?: string | null;
15889
+ series?: string | null;
15890
+ episode?: string | null;
15891
+ episodeType?: string | null;
15892
+ season?: string | null;
15893
+ publisher?: string | null;
15894
+ copyright?: string | null;
15895
+ genre?: string | null;
15896
+ title?: string | null;
15897
+ description?: string | null;
15898
+ bitrate?: number | null;
15899
+ channels?: number | null;
15900
+ sampleRate?: number | null;
15901
+ bitsPerSample?: number | null;
15902
+ duration?: any | null;
15903
+ } | null;
15904
+ image?: {
15905
+ __typename?: 'ImageMetadata';
15906
+ width?: number | null;
15907
+ height?: number | null;
15908
+ resolutionX?: number | null;
15909
+ resolutionY?: number | null;
15910
+ bitsPerComponent?: number | null;
15911
+ components?: number | null;
15912
+ projectionType?: ImageProjectionTypes | null;
15913
+ orientation?: OrientationTypes | null;
15914
+ description?: string | null;
15915
+ make?: string | null;
15916
+ model?: string | null;
15917
+ software?: string | null;
15918
+ lens?: string | null;
15919
+ focalLength?: number | null;
15920
+ exposureTime?: string | null;
15921
+ fNumber?: string | null;
15922
+ iso?: string | null;
15923
+ heading?: number | null;
15924
+ pitch?: number | null;
15925
+ } | null;
15926
+ document?: {
15927
+ __typename?: 'DocumentMetadata';
15928
+ title?: string | null;
15929
+ subject?: string | null;
15930
+ summary?: string | null;
15931
+ author?: string | null;
15932
+ publisher?: string | null;
15933
+ description?: string | null;
15934
+ keywords?: Array<string | null> | null;
15935
+ pageCount?: number | null;
15936
+ worksheetCount?: number | null;
15937
+ slideCount?: number | null;
15938
+ wordCount?: number | null;
15939
+ lineCount?: number | null;
15940
+ paragraphCount?: number | null;
15941
+ isEncrypted?: boolean | null;
15942
+ hasDigitalSignature?: boolean | null;
15943
+ } | null;
15944
+ email?: {
15945
+ __typename?: 'EmailMetadata';
15946
+ identifier?: string | null;
15947
+ threadIdentifier?: string | null;
15948
+ subject?: string | null;
15949
+ labels?: Array<string | null> | null;
15950
+ sensitivity?: MailSensitivity | null;
15951
+ priority?: MailPriority | null;
15952
+ importance?: MailImportance | null;
15953
+ from?: Array<{
15954
+ __typename?: 'PersonReference';
15955
+ name?: string | null;
15956
+ email?: string | null;
15957
+ givenName?: string | null;
15958
+ familyName?: string | null;
15959
+ } | null> | null;
15960
+ to?: Array<{
15961
+ __typename?: 'PersonReference';
15962
+ name?: string | null;
15963
+ email?: string | null;
15964
+ givenName?: string | null;
15965
+ familyName?: string | null;
15966
+ } | null> | null;
15967
+ cc?: Array<{
15968
+ __typename?: 'PersonReference';
15969
+ name?: string | null;
15970
+ email?: string | null;
15971
+ givenName?: string | null;
15972
+ familyName?: string | null;
15973
+ } | null> | null;
15974
+ bcc?: Array<{
15975
+ __typename?: 'PersonReference';
15976
+ name?: string | null;
15977
+ email?: string | null;
15978
+ givenName?: string | null;
15979
+ familyName?: string | null;
15980
+ } | null> | null;
15981
+ } | null;
15982
+ issue?: {
15983
+ __typename?: 'IssueMetadata';
15984
+ identifier?: string | null;
15985
+ title?: string | null;
15986
+ project?: string | null;
15987
+ team?: string | null;
15988
+ status?: string | null;
15989
+ priority?: string | null;
15990
+ type?: string | null;
15991
+ labels?: Array<string | null> | null;
15992
+ } | null;
15993
+ package?: {
15994
+ __typename?: 'PackageMetadata';
15995
+ fileCount?: number | null;
15996
+ folderCount?: number | null;
15997
+ isEncrypted?: boolean | null;
15998
+ } | null;
15999
+ language?: {
16000
+ __typename?: 'LanguageMetadata';
16001
+ languages?: Array<string | null> | null;
16002
+ } | null;
16003
+ parent?: {
16004
+ __typename?: 'Content';
16005
+ id: string;
16006
+ name: string;
16007
+ } | null;
16008
+ children?: Array<{
16009
+ __typename?: 'Content';
16010
+ id: string;
16011
+ name: string;
16012
+ } | null> | null;
16013
+ feed?: {
16014
+ __typename?: 'Feed';
16015
+ id: string;
16016
+ name: string;
16017
+ } | null;
16018
+ collections?: Array<{
16019
+ __typename?: 'Collection';
16020
+ id: string;
16021
+ name: string;
16022
+ } | null> | null;
16023
+ links?: Array<{
16024
+ __typename?: 'LinkReference';
16025
+ uri?: any | null;
16026
+ linkType?: LinkTypes | null;
16027
+ }> | null;
16028
+ observations?: Array<{
16029
+ __typename?: 'Observation';
16030
+ id: string;
16031
+ type: ObservableTypes;
16032
+ relatedType?: ObservableTypes | null;
16033
+ relation?: string | null;
16034
+ state: EntityState;
16035
+ observable: {
16036
+ __typename?: 'NamedEntityReference';
16037
+ id: string;
16038
+ name?: string | null;
16039
+ };
16040
+ related?: {
16041
+ __typename?: 'NamedEntityReference';
16042
+ id: string;
16043
+ name?: string | null;
16044
+ } | null;
16045
+ occurrences?: Array<{
16046
+ __typename?: 'ObservationOccurrence';
16047
+ type?: OccurrenceTypes | null;
16048
+ confidence?: number | null;
16049
+ startTime?: any | null;
16050
+ endTime?: any | null;
16051
+ pageIndex?: number | null;
16052
+ boundingBox?: {
16053
+ __typename?: 'BoundingBox';
16054
+ left?: number | null;
16055
+ top?: number | null;
16056
+ width?: number | null;
16057
+ height?: number | null;
16058
+ } | null;
16059
+ } | null> | null;
16060
+ } | null> | null;
16061
+ workflow?: {
16062
+ __typename?: 'Workflow';
16063
+ id: string;
16064
+ name: string;
16065
+ } | null;
16066
+ pages?: Array<{
16067
+ __typename?: 'TextPage';
16068
+ index?: number | null;
16069
+ text?: string | null;
16070
+ relevance?: number | null;
16071
+ images?: Array<{
16072
+ __typename?: 'ImageChunk';
16073
+ id?: string | null;
16074
+ mimeType?: string | null;
16075
+ data?: string | null;
16076
+ left?: number | null;
16077
+ right?: number | null;
16078
+ top?: number | null;
16079
+ bottom?: number | null;
16080
+ } | null> | null;
16081
+ chunks?: Array<{
16082
+ __typename?: 'TextChunk';
16083
+ index?: number | null;
16084
+ pageIndex?: number | null;
16085
+ rowIndex?: number | null;
16086
+ columnIndex?: number | null;
16087
+ confidence?: number | null;
16088
+ text?: string | null;
16089
+ role?: TextRoles | null;
16090
+ language?: string | null;
16091
+ relevance?: number | null;
16092
+ } | null> | null;
16093
+ }> | null;
16094
+ segments?: Array<{
16095
+ __typename?: 'TextSegment';
16096
+ startTime?: any | null;
16097
+ endTime?: any | null;
16098
+ text?: string | null;
16099
+ relevance?: number | null;
16100
+ }> | null;
16101
+ frames?: Array<{
16102
+ __typename?: 'TextFrame';
16103
+ index?: number | null;
16104
+ description?: string | null;
16105
+ text?: string | null;
16106
+ relevance?: number | null;
16107
+ }> | null;
16108
+ } | null> | null;
16109
+ } | null;
16110
+ };
15787
16111
  export type PublishContentsMutationVariables = Exact<{
15788
16112
  summaryPrompt?: InputMaybe<Scalars['String']['input']>;
15789
16113
  publishPrompt: Scalars['String']['input'];
@@ -13,9 +13,11 @@ export declare class UIEventAdapter {
13
13
  private tokenCount;
14
14
  private currentMessage;
15
15
  private isStreaming;
16
+ private conversationStartTime;
16
17
  private streamStartTime;
17
18
  private firstTokenTime;
18
19
  private lastTokenTime;
20
+ private tokenDelays;
19
21
  private activeToolCalls;
20
22
  private lastUpdateTime;
21
23
  private updateTimer?;
@@ -12,9 +12,11 @@ export class UIEventAdapter {
12
12
  tokenCount = 0;
13
13
  currentMessage = "";
14
14
  isStreaming = false;
15
- streamStartTime = 0;
15
+ conversationStartTime = 0; // When user sent the message
16
+ streamStartTime = 0; // When streaming actually began
16
17
  firstTokenTime = 0;
17
18
  lastTokenTime = 0;
19
+ tokenDelays = [];
18
20
  activeToolCalls = new Map();
19
21
  lastUpdateTime = 0;
20
22
  updateTimer;
@@ -27,6 +29,7 @@ export class UIEventAdapter {
27
29
  this.smoothingDelay = options.smoothingDelay ?? 30;
28
30
  this.model = options.model;
29
31
  this.modelService = options.modelService;
32
+ this.conversationStartTime = Date.now(); // Capture when conversation began
30
33
  if (options.smoothingEnabled) {
31
34
  this.chunkBuffer = new ChunkBuffer(options.chunkingStrategy || "word");
32
35
  }
@@ -55,7 +58,7 @@ export class UIEventAdapter {
55
58
  this.handleToolCallComplete(event.toolCall);
56
59
  break;
57
60
  case "complete":
58
- this.handleComplete();
61
+ this.handleComplete(event.tokens);
59
62
  break;
60
63
  case "error":
61
64
  this.handleError(event.error);
@@ -94,6 +97,8 @@ export class UIEventAdapter {
94
97
  this.streamStartTime = Date.now();
95
98
  this.firstTokenTime = 0;
96
99
  this.lastTokenTime = 0;
100
+ this.tokenCount = 0;
101
+ this.tokenDelays = [];
97
102
  this.emitUIEvent({
98
103
  type: "conversation_started",
99
104
  conversationId,
@@ -107,7 +112,12 @@ export class UIEventAdapter {
107
112
  if (this.firstTokenTime === 0) {
108
113
  this.firstTokenTime = now;
109
114
  }
115
+ // Track inter-token delays
116
+ if (this.lastTokenTime > 0) {
117
+ this.tokenDelays.push(now - this.lastTokenTime);
118
+ }
110
119
  this.lastTokenTime = now;
120
+ this.tokenCount++;
111
121
  if (this.chunkBuffer) {
112
122
  const chunks = this.chunkBuffer.addToken(token);
113
123
  // Add chunks to queue for all chunking modes (character, word, sentence)
@@ -165,7 +175,7 @@ export class UIEventAdapter {
165
175
  });
166
176
  }
167
177
  }
168
- handleComplete() {
178
+ handleComplete(tokens) {
169
179
  // Clear any pending updates
170
180
  if (this.updateTimer) {
171
181
  globalThis.clearTimeout(this.updateTimer);
@@ -200,7 +210,7 @@ export class UIEventAdapter {
200
210
  role: ConversationRoleTypes.Assistant,
201
211
  message: this.currentMessage,
202
212
  timestamp: new Date().toISOString(),
203
- tokens: undefined, // Will be set by caller if available
213
+ tokens: tokens, // Now we have the actual LLM token count!
204
214
  toolCalls: Array.from(this.activeToolCalls.values()).map((t) => t.toolCall),
205
215
  model: this.model,
206
216
  modelService: this.modelService,
@@ -222,9 +232,32 @@ export class UIEventAdapter {
222
232
  }
223
233
  }
224
234
  }
235
+ // Build final metrics
236
+ const completionTime = Date.now();
237
+ const finalMetrics = {
238
+ totalTime: this.streamStartTime > 0 ? completionTime - this.streamStartTime : 0,
239
+ conversationDuration: this.conversationStartTime > 0 ? completionTime - this.conversationStartTime : 0,
240
+ };
241
+ // Add TTFT if we have it
242
+ if (this.firstTokenTime > 0 && this.streamStartTime > 0) {
243
+ finalMetrics.ttft = this.firstTokenTime - this.streamStartTime;
244
+ }
245
+ // Add token counts
246
+ if (this.tokenCount > 0) {
247
+ finalMetrics.tokenCount = this.tokenCount; // Streaming chunks
248
+ }
249
+ if (tokens) {
250
+ finalMetrics.llmTokens = tokens; // Actual LLM tokens used
251
+ }
252
+ // Calculate average token delay
253
+ if (this.tokenDelays.length > 0) {
254
+ const avgDelay = this.tokenDelays.reduce((a, b) => a + b, 0) / this.tokenDelays.length;
255
+ finalMetrics.avgTokenDelay = Math.round(avgDelay);
256
+ }
225
257
  this.emitUIEvent({
226
258
  type: "conversation_completed",
227
259
  message: finalMessage,
260
+ metrics: finalMetrics,
228
261
  });
229
262
  }
230
263
  handleError(error) {
@@ -330,10 +363,30 @@ export class UIEventAdapter {
330
363
  message.completionTime = elapsedTime / 1000;
331
364
  }
332
365
  }
366
+ // Build metrics object
367
+ const now = Date.now();
368
+ const metrics = {
369
+ elapsedTime: this.streamStartTime > 0 ? now - this.streamStartTime : 0,
370
+ conversationDuration: this.conversationStartTime > 0 ? now - this.conversationStartTime : 0,
371
+ };
372
+ // Add TTFT if we have it
373
+ if (this.firstTokenTime > 0 && this.streamStartTime > 0) {
374
+ metrics.ttft = this.firstTokenTime - this.streamStartTime;
375
+ }
376
+ // Add token count if available
377
+ if (this.tokenCount > 0) {
378
+ metrics.tokenCount = this.tokenCount;
379
+ }
380
+ // Calculate average token delay
381
+ if (this.tokenDelays.length > 0) {
382
+ const avgDelay = this.tokenDelays.reduce((a, b) => a + b, 0) / this.tokenDelays.length;
383
+ metrics.avgTokenDelay = Math.round(avgDelay);
384
+ }
333
385
  this.emitUIEvent({
334
386
  type: "message_update",
335
387
  message,
336
388
  isStreaming,
389
+ metrics,
337
390
  });
338
391
  }
339
392
  emitUIEvent(event) {
@@ -36,6 +36,7 @@ export type StreamEvent = {
36
36
  type: "complete";
37
37
  messageId?: string;
38
38
  conversationId?: string;
39
+ tokens?: number;
39
40
  } | {
40
41
  type: "error";
41
42
  error: string;
@@ -17,6 +17,13 @@ export type AgentStreamEvent = {
17
17
  message: string;
18
18
  };
19
19
  isStreaming: boolean;
20
+ metrics?: {
21
+ ttft?: number;
22
+ elapsedTime: number;
23
+ conversationDuration: number;
24
+ tokenCount?: number;
25
+ avgTokenDelay?: number;
26
+ };
20
27
  } | {
21
28
  type: "tool_update";
22
29
  toolCall: ConversationToolCall;
@@ -26,6 +33,14 @@ export type AgentStreamEvent = {
26
33
  } | {
27
34
  type: "conversation_completed";
28
35
  message: ConversationMessage;
36
+ metrics?: {
37
+ ttft?: number;
38
+ totalTime: number;
39
+ conversationDuration: number;
40
+ tokenCount?: number;
41
+ llmTokens?: number;
42
+ avgTokenDelay?: number;
43
+ };
29
44
  } | {
30
45
  type: "error";
31
46
  error: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphlit-client",
3
- "version": "1.0.20250612009",
3
+ "version": "1.0.20250613002",
4
4
  "description": "Graphlit API Client for TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/client.js",