@letta-ai/letta-react 0.0.6 → 0.0.8

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.
@@ -46,13 +46,21 @@ function dedupeMessages(messages) {
46
46
  return false;
47
47
  }, []);
48
48
  }
49
+ function messageCreateToMessageUnion(messages) {
50
+ return messages.map((message, idx) => ({
51
+ messageType: message.role === 'user' ? 'user_message' : 'system_message',
52
+ content: message.content,
53
+ date: new Date(),
54
+ id: `autogenerated_${Date.now()}_${idx}`,
55
+ }));
56
+ }
49
57
  export function useAgentMessages(options) {
50
58
  const { client = {}, method = 'stream', messageOptions = {}, limit = 20, agentId, } = options;
51
59
  const localClient = useLettaClient(client);
52
60
  const [localMessages, setLocalMessages] = useCachedState(`messages-${agentId}`, {
53
61
  messages: [],
54
62
  });
55
- const hasInitialLoaded = useRef(false);
63
+ const agentIdMessagesLoadedRef = useRef(false);
56
64
  const [isLoading, setIsLoading] = useState(true);
57
65
  const [isFetching, setIsFetching] = useState(false);
58
66
  const [loadingError, setLoadingError] = useState(null);
@@ -60,26 +68,39 @@ export function useAgentMessages(options) {
60
68
  const [sendingError, setSendingError] = useState(null);
61
69
  const sendNonStreamedMessage = useCallback(function sendNonStreamedMessage(sendMessagePayload) {
62
70
  return __awaiter(this, void 0, void 0, function* () {
71
+ let originalMessageState = localMessages;
63
72
  try {
64
73
  setIsSending(true);
65
74
  setSendingError(null);
75
+ const newMessages = messageCreateToMessageUnion(sendMessagePayload.messages);
76
+ setLocalMessages((prevState) => {
77
+ originalMessageState = prevState;
78
+ return Object.assign(Object.assign({}, prevState), { messages: [...prevState.messages, ...newMessages] });
79
+ });
66
80
  const response = yield localClient.agents.messages.create(agentId, Object.assign(Object.assign({}, sendMessagePayload), messageOptions));
67
- setLocalMessages((prevState) => (Object.assign(Object.assign({}, prevState), { messages: [...response.messages, ...prevState.messages] })));
81
+ setLocalMessages((prevState) => (Object.assign(Object.assign({}, prevState), { messages: [...prevState.messages, ...response.messages] })));
68
82
  }
69
83
  catch (e) {
84
+ setLocalMessages(originalMessageState);
70
85
  setSendingError(e);
71
86
  }
72
87
  finally {
73
88
  setIsSending(false);
74
89
  }
75
90
  });
76
- }, [localClient]);
91
+ }, [localClient, localMessages]);
77
92
  const sendStreamedMessage = useCallback(function sendStreamedMessage(sendMessagePayload) {
78
93
  return __awaiter(this, void 0, void 0, function* () {
79
94
  var _a, e_1, _b, _c;
95
+ let originalMessageState = localMessages;
80
96
  try {
81
97
  setIsSending(true);
82
98
  setSendingError(null);
99
+ const newMessages = messageCreateToMessageUnion(sendMessagePayload.messages);
100
+ setLocalMessages((prevState) => {
101
+ originalMessageState = prevState;
102
+ return Object.assign(Object.assign({}, prevState), { messages: [...prevState.messages, ...newMessages] });
103
+ });
83
104
  const response = yield localClient.agents.messages.createStream(agentId, Object.assign(Object.assign(Object.assign({}, sendMessagePayload), messageOptions), { streamTokens: true }));
84
105
  try {
85
106
  for (var _d = true, response_1 = __asyncValues(response), response_1_1; response_1_1 = yield response_1.next(), _a = response_1_1.done, !_a; _d = true) {
@@ -167,13 +188,17 @@ export function useAgentMessages(options) {
167
188
  });
168
189
  }, [localClient]);
169
190
  const sendMessage = useCallback(function sendMessage(payload) {
191
+ const filteredPayload = Object.assign(Object.assign({}, payload), { messages: payload.messages.filter((e) => !!e.content) });
192
+ if (filteredPayload.messages.length === 0) {
193
+ return;
194
+ }
170
195
  if (isSending) {
171
196
  return;
172
197
  }
173
198
  if (method === 'stream') {
174
- return sendStreamedMessage(payload);
199
+ return sendStreamedMessage(filteredPayload);
175
200
  }
176
- return sendNonStreamedMessage(payload);
201
+ return sendNonStreamedMessage(filteredPayload);
177
202
  }, [method, isSending, sendStreamedMessage, sendNonStreamedMessage]);
178
203
  const getMessages = useCallback(function getMessages(before) {
179
204
  return __awaiter(this, void 0, void 0, function* () {
@@ -205,12 +230,11 @@ export function useAgentMessages(options) {
205
230
  return getMessages(nextCursor);
206
231
  }), [getMessages]);
207
232
  useEffect(() => {
208
- if (hasInitialLoaded.current) {
209
- return;
233
+ if (!agentIdMessagesLoadedRef.current) {
234
+ setIsLoading(true);
235
+ void getMessages();
236
+ agentIdMessagesLoadedRef.current = true;
210
237
  }
211
- hasInitialLoaded.current = true;
212
- setIsLoading(true);
213
- getMessages();
214
238
  }, []);
215
239
  return {
216
240
  messages: localMessages.messages,
@@ -0,0 +1,13 @@
1
+ import type { Passage } from '@letta-ai/letta-client/api';
2
+ import type { LettaClient } from '@letta-ai/letta-client';
3
+ interface UseAgentStateOptions {
4
+ client?: LettaClient.Options;
5
+ agentId: string;
6
+ }
7
+ export declare function useAgentPassages(options: UseAgentStateOptions): {
8
+ isLoading: boolean;
9
+ isLoadingError: unknown;
10
+ passages: Passage[] | undefined;
11
+ refresh: () => Promise<void>;
12
+ };
13
+ export {};
@@ -0,0 +1,45 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { useLettaClient } from '../useLettaClient/useLettaClient';
11
+ import { useCachedState } from '../useCachedState/useCachedState';
12
+ import { useCallback, useEffect, useRef, useState } from 'react';
13
+ export function useAgentPassages(options) {
14
+ const { client, agentId } = options;
15
+ const localClient = useLettaClient(client);
16
+ const loadedAgentId = useRef(null);
17
+ const [localState, setLocalState] = useCachedState(`agent-passages-${agentId}`, undefined);
18
+ const [isLoading, setIsLoading] = useState(true);
19
+ const [isLoadingError, setIsLoadingError] = useState(null);
20
+ const getAgentPassages = useCallback(() => __awaiter(this, void 0, void 0, function* () {
21
+ try {
22
+ const state = yield localClient.agents.passages.list(agentId);
23
+ setLocalState(state);
24
+ }
25
+ catch (error) {
26
+ setIsLoadingError(error);
27
+ }
28
+ finally {
29
+ setIsLoading(false);
30
+ }
31
+ }), [agentId, localClient, setLocalState]);
32
+ useEffect(() => {
33
+ if (agentId !== loadedAgentId.current) {
34
+ setIsLoading(true);
35
+ void getAgentPassages();
36
+ loadedAgentId.current = agentId;
37
+ }
38
+ }, [agentId, getAgentPassages]);
39
+ return {
40
+ isLoading,
41
+ isLoadingError,
42
+ passages: localState,
43
+ refresh: getAgentPassages,
44
+ };
45
+ }
@@ -1,12 +1,15 @@
1
1
  import type { LettaClient } from '@letta-ai/letta-client';
2
- import type { AgentState } from '@letta-ai/letta-client/api';
2
+ import type { AgentState, UpdateAgent } from '@letta-ai/letta-client/api';
3
3
  interface UseAgentStateOptions {
4
4
  client?: LettaClient.Options;
5
5
  agentId: string;
6
6
  }
7
7
  export declare function useAgentState(options: UseAgentStateOptions): {
8
8
  isLoading: boolean;
9
- error: unknown;
9
+ updateAgentState: (state: Partial<UpdateAgent>) => Promise<void>;
10
+ isUpdating: boolean;
11
+ updatingError: unknown;
12
+ loadingError: unknown;
10
13
  agentState: AgentState | undefined;
11
14
  refresh: () => Promise<void>;
12
15
  };
@@ -15,8 +15,10 @@ export function useAgentState(options) {
15
15
  const localClient = useLettaClient(client);
16
16
  const [localState, setLocalState] = useCachedState(`agent-state-${agentId}`, undefined);
17
17
  const [isLoading, setIsLoading] = useState(true);
18
+ const [isUpdating, setIsUpdating] = useState(false);
19
+ const [updatingError, setUpdatingError] = useState(null);
18
20
  const [loadingError, setLoadingError] = useState(null);
19
- const hasInitialLoaded = useRef(false);
21
+ const loadedAgentId = useRef(null);
20
22
  const getAgentState = useCallback(() => __awaiter(this, void 0, void 0, function* () {
21
23
  try {
22
24
  const state = yield localClient.agents.retrieve(agentId);
@@ -29,17 +31,32 @@ export function useAgentState(options) {
29
31
  setIsLoading(false);
30
32
  }
31
33
  }), [agentId, localClient, setLocalState]);
34
+ const updateAgentState = useCallback((state) => __awaiter(this, void 0, void 0, function* () {
35
+ setIsUpdating(true);
36
+ try {
37
+ const response = yield localClient.agents.modify(agentId, state);
38
+ setLocalState(response);
39
+ }
40
+ catch (error) {
41
+ setUpdatingError(error);
42
+ }
43
+ finally {
44
+ setIsUpdating(false);
45
+ }
46
+ }), [agentId, localClient, setLocalState]);
32
47
  useEffect(() => {
33
- if (hasInitialLoaded.current) {
34
- return;
48
+ if (agentId !== loadedAgentId.current) {
49
+ setIsLoading(true);
50
+ void getAgentState();
51
+ loadedAgentId.current = agentId;
35
52
  }
36
- hasInitialLoaded.current = true;
37
- setIsLoading(true);
38
- getAgentState();
39
- }, []);
53
+ }, [agentId]);
40
54
  return {
41
55
  isLoading,
42
- error: loadingError,
56
+ updateAgentState,
57
+ isUpdating,
58
+ updatingError,
59
+ loadingError,
43
60
  agentState: localState,
44
61
  refresh: getAgentState,
45
62
  };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { LettaProvider } from './hooks/useGlobalLettaConfig/useGlobalLettaConfig';
2
2
  export { useAgentMessages } from './hooks/useAgentMessages/useAgentMessages';
3
3
  export { useAgentState } from './hooks/useAgentState/useAgentState';
4
+ export { useAgentPassages } from './hooks/useAgentPassages/useAgentPassages';
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { LettaProvider } from './hooks/useGlobalLettaConfig/useGlobalLettaConfig';
2
2
  export { useAgentMessages } from './hooks/useAgentMessages/useAgentMessages';
3
3
  export { useAgentState } from './hooks/useAgentState/useAgentState';
4
+ export { useAgentPassages } from './hooks/useAgentPassages/useAgentPassages';
@@ -1,7 +1,11 @@
1
1
  import React, { FormEvent, useCallback } from 'react';
2
2
  import { useState } from 'react';
3
3
  import './App.css';
4
- import { useAgentMessages, useAgentState } from '@letta-ai/letta-react';
4
+ import {
5
+ useAgentMessages,
6
+ useAgentPassages,
7
+ useAgentState,
8
+ } from '@letta-ai/letta-react';
5
9
 
6
10
  function App() {
7
11
  const [messageToSend, setMessageToSend] = useState<string>('');
@@ -14,11 +18,15 @@ function App() {
14
18
  isSending,
15
19
  sendMessage,
16
20
  } = useAgentMessages({
17
- agentId: 'agent-ed85493d-2164-4404-b52e-119ccbc987b4',
21
+ agentId: 'agent-0d07e901-64de-4bbd-8a7c-268ce88bc6cb',
22
+ });
23
+
24
+ const { passages, isLoading: isPassagesLoading } = useAgentPassages({
25
+ agentId: 'agent-0d07e901-64de-4bbd-8a7c-268ce88bc6cb',
18
26
  });
19
27
 
20
28
  const { agentState } = useAgentState({
21
- agentId: 'agent-ed85493d-2164-4404-b52e-119ccbc987b4',
29
+ agentId: 'agent-0d07e901-64de-4bbd-8a7c-268ce88bc6cb',
22
30
  });
23
31
 
24
32
  const handleSubmit = useCallback(
@@ -42,6 +50,16 @@ function App() {
42
50
  return (
43
51
  <main>
44
52
  <header>Talking to: {agentState?.name}</header>
53
+ {isPassagesLoading ? (
54
+ <div>Loading passages!</div>
55
+ ) : (
56
+ <ul className="passages">
57
+ {passages?.map((passage) => (
58
+ <li key={passage.id}>{passage.text}</li>
59
+ ))}
60
+ </ul>
61
+ )}
62
+
45
63
  {hasOlderMessages && (
46
64
  <button onClick={fetchOlderMessages} disabled={isFetching}>
47
65
  {isFetching ? 'Loading older messages' : 'Load older messages'}
@@ -57,7 +75,6 @@ function App() {
57
75
  return (
58
76
  <li key={`${message.id}${message.messageType}`}>
59
77
  {message.content}
60
- {message.id}
61
78
  </li>
62
79
  );
63
80
  }
@@ -46,13 +46,21 @@ function dedupeMessages(messages) {
46
46
  return false;
47
47
  }, []);
48
48
  }
49
+ function messageCreateToMessageUnion(messages) {
50
+ return messages.map((message, idx) => ({
51
+ messageType: message.role === 'user' ? 'user_message' : 'system_message',
52
+ content: message.content,
53
+ date: new Date(),
54
+ id: `autogenerated_${Date.now()}_${idx}`,
55
+ }));
56
+ }
49
57
  export function useAgentMessages(options) {
50
58
  const { client = {}, method = 'stream', messageOptions = {}, limit = 20, agentId, } = options;
51
59
  const localClient = useLettaClient(client);
52
60
  const [localMessages, setLocalMessages] = useCachedState(`messages-${agentId}`, {
53
61
  messages: [],
54
62
  });
55
- const hasInitialLoaded = useRef(false);
63
+ const agentIdMessagesLoadedRef = useRef(false);
56
64
  const [isLoading, setIsLoading] = useState(true);
57
65
  const [isFetching, setIsFetching] = useState(false);
58
66
  const [loadingError, setLoadingError] = useState(null);
@@ -60,26 +68,39 @@ export function useAgentMessages(options) {
60
68
  const [sendingError, setSendingError] = useState(null);
61
69
  const sendNonStreamedMessage = useCallback(function sendNonStreamedMessage(sendMessagePayload) {
62
70
  return __awaiter(this, void 0, void 0, function* () {
71
+ let originalMessageState = localMessages;
63
72
  try {
64
73
  setIsSending(true);
65
74
  setSendingError(null);
75
+ const newMessages = messageCreateToMessageUnion(sendMessagePayload.messages);
76
+ setLocalMessages((prevState) => {
77
+ originalMessageState = prevState;
78
+ return Object.assign(Object.assign({}, prevState), { messages: [...prevState.messages, ...newMessages] });
79
+ });
66
80
  const response = yield localClient.agents.messages.create(agentId, Object.assign(Object.assign({}, sendMessagePayload), messageOptions));
67
- setLocalMessages((prevState) => (Object.assign(Object.assign({}, prevState), { messages: [...response.messages, ...prevState.messages] })));
81
+ setLocalMessages((prevState) => (Object.assign(Object.assign({}, prevState), { messages: [...prevState.messages, ...response.messages] })));
68
82
  }
69
83
  catch (e) {
84
+ setLocalMessages(originalMessageState);
70
85
  setSendingError(e);
71
86
  }
72
87
  finally {
73
88
  setIsSending(false);
74
89
  }
75
90
  });
76
- }, [localClient]);
91
+ }, [localClient, localMessages]);
77
92
  const sendStreamedMessage = useCallback(function sendStreamedMessage(sendMessagePayload) {
78
93
  return __awaiter(this, void 0, void 0, function* () {
79
94
  var _a, e_1, _b, _c;
95
+ let originalMessageState = localMessages;
80
96
  try {
81
97
  setIsSending(true);
82
98
  setSendingError(null);
99
+ const newMessages = messageCreateToMessageUnion(sendMessagePayload.messages);
100
+ setLocalMessages((prevState) => {
101
+ originalMessageState = prevState;
102
+ return Object.assign(Object.assign({}, prevState), { messages: [...prevState.messages, ...newMessages] });
103
+ });
83
104
  const response = yield localClient.agents.messages.createStream(agentId, Object.assign(Object.assign(Object.assign({}, sendMessagePayload), messageOptions), { streamTokens: true }));
84
105
  try {
85
106
  for (var _d = true, response_1 = __asyncValues(response), response_1_1; response_1_1 = yield response_1.next(), _a = response_1_1.done, !_a; _d = true) {
@@ -167,13 +188,17 @@ export function useAgentMessages(options) {
167
188
  });
168
189
  }, [localClient]);
169
190
  const sendMessage = useCallback(function sendMessage(payload) {
191
+ const filteredPayload = Object.assign(Object.assign({}, payload), { messages: payload.messages.filter((e) => !!e.content) });
192
+ if (filteredPayload.messages.length === 0) {
193
+ return;
194
+ }
170
195
  if (isSending) {
171
196
  return;
172
197
  }
173
198
  if (method === 'stream') {
174
- return sendStreamedMessage(payload);
199
+ return sendStreamedMessage(filteredPayload);
175
200
  }
176
- return sendNonStreamedMessage(payload);
201
+ return sendNonStreamedMessage(filteredPayload);
177
202
  }, [method, isSending, sendStreamedMessage, sendNonStreamedMessage]);
178
203
  const getMessages = useCallback(function getMessages(before) {
179
204
  return __awaiter(this, void 0, void 0, function* () {
@@ -205,12 +230,11 @@ export function useAgentMessages(options) {
205
230
  return getMessages(nextCursor);
206
231
  }), [getMessages]);
207
232
  useEffect(() => {
208
- if (hasInitialLoaded.current) {
209
- return;
233
+ if (!agentIdMessagesLoadedRef.current) {
234
+ setIsLoading(true);
235
+ void getMessages();
236
+ agentIdMessagesLoadedRef.current = true;
210
237
  }
211
- hasInitialLoaded.current = true;
212
- setIsLoading(true);
213
- getMessages();
214
238
  }, []);
215
239
  return {
216
240
  messages: localMessages.messages,
@@ -0,0 +1,13 @@
1
+ import type { Passage } from '@letta-ai/letta-client/api';
2
+ import type { LettaClient } from '@letta-ai/letta-client';
3
+ interface UseAgentStateOptions {
4
+ client?: LettaClient.Options;
5
+ agentId: string;
6
+ }
7
+ export declare function useAgentPassages(options: UseAgentStateOptions): {
8
+ isLoading: boolean;
9
+ isLoadingError: unknown;
10
+ passages: Passage[] | undefined;
11
+ refresh: () => Promise<void>;
12
+ };
13
+ export {};
@@ -0,0 +1,45 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { useLettaClient } from '../useLettaClient/useLettaClient';
11
+ import { useCachedState } from '../useCachedState/useCachedState';
12
+ import { useCallback, useEffect, useRef, useState } from 'react';
13
+ export function useAgentPassages(options) {
14
+ const { client, agentId } = options;
15
+ const localClient = useLettaClient(client);
16
+ const loadedAgentId = useRef(null);
17
+ const [localState, setLocalState] = useCachedState(`agent-passages-${agentId}`, undefined);
18
+ const [isLoading, setIsLoading] = useState(true);
19
+ const [isLoadingError, setIsLoadingError] = useState(null);
20
+ const getAgentPassages = useCallback(() => __awaiter(this, void 0, void 0, function* () {
21
+ try {
22
+ const state = yield localClient.agents.passages.list(agentId);
23
+ setLocalState(state);
24
+ }
25
+ catch (error) {
26
+ setIsLoadingError(error);
27
+ }
28
+ finally {
29
+ setIsLoading(false);
30
+ }
31
+ }), [agentId, localClient, setLocalState]);
32
+ useEffect(() => {
33
+ if (agentId !== loadedAgentId.current) {
34
+ setIsLoading(true);
35
+ void getAgentPassages();
36
+ loadedAgentId.current = agentId;
37
+ }
38
+ }, [agentId, getAgentPassages]);
39
+ return {
40
+ isLoading,
41
+ isLoadingError,
42
+ passages: localState,
43
+ refresh: getAgentPassages,
44
+ };
45
+ }
@@ -1,12 +1,15 @@
1
1
  import type { LettaClient } from '@letta-ai/letta-client';
2
- import type { AgentState } from '@letta-ai/letta-client/api';
2
+ import type { AgentState, UpdateAgent } from '@letta-ai/letta-client/api';
3
3
  interface UseAgentStateOptions {
4
4
  client?: LettaClient.Options;
5
5
  agentId: string;
6
6
  }
7
7
  export declare function useAgentState(options: UseAgentStateOptions): {
8
8
  isLoading: boolean;
9
- error: unknown;
9
+ updateAgentState: (state: Partial<UpdateAgent>) => Promise<void>;
10
+ isUpdating: boolean;
11
+ updatingError: unknown;
12
+ loadingError: unknown;
10
13
  agentState: AgentState | undefined;
11
14
  refresh: () => Promise<void>;
12
15
  };
@@ -15,8 +15,10 @@ export function useAgentState(options) {
15
15
  const localClient = useLettaClient(client);
16
16
  const [localState, setLocalState] = useCachedState(`agent-state-${agentId}`, undefined);
17
17
  const [isLoading, setIsLoading] = useState(true);
18
+ const [isUpdating, setIsUpdating] = useState(false);
19
+ const [updatingError, setUpdatingError] = useState(null);
18
20
  const [loadingError, setLoadingError] = useState(null);
19
- const hasInitialLoaded = useRef(false);
21
+ const loadedAgentId = useRef(null);
20
22
  const getAgentState = useCallback(() => __awaiter(this, void 0, void 0, function* () {
21
23
  try {
22
24
  const state = yield localClient.agents.retrieve(agentId);
@@ -29,17 +31,32 @@ export function useAgentState(options) {
29
31
  setIsLoading(false);
30
32
  }
31
33
  }), [agentId, localClient, setLocalState]);
34
+ const updateAgentState = useCallback((state) => __awaiter(this, void 0, void 0, function* () {
35
+ setIsUpdating(true);
36
+ try {
37
+ const response = yield localClient.agents.modify(agentId, state);
38
+ setLocalState(response);
39
+ }
40
+ catch (error) {
41
+ setUpdatingError(error);
42
+ }
43
+ finally {
44
+ setIsUpdating(false);
45
+ }
46
+ }), [agentId, localClient, setLocalState]);
32
47
  useEffect(() => {
33
- if (hasInitialLoaded.current) {
34
- return;
48
+ if (agentId !== loadedAgentId.current) {
49
+ setIsLoading(true);
50
+ void getAgentState();
51
+ loadedAgentId.current = agentId;
35
52
  }
36
- hasInitialLoaded.current = true;
37
- setIsLoading(true);
38
- getAgentState();
39
- }, []);
53
+ }, [agentId]);
40
54
  return {
41
55
  isLoading,
42
- error: loadingError,
56
+ updateAgentState,
57
+ isUpdating,
58
+ updatingError,
59
+ loadingError,
43
60
  agentState: localState,
44
61
  refresh: getAgentState,
45
62
  };
package/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { LettaProvider } from './hooks/useGlobalLettaConfig/useGlobalLettaConfig';
2
2
  export { useAgentMessages } from './hooks/useAgentMessages/useAgentMessages';
3
3
  export { useAgentState } from './hooks/useAgentState/useAgentState';
4
+ export { useAgentPassages } from './hooks/useAgentPassages/useAgentPassages';
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { LettaProvider } from './hooks/useGlobalLettaConfig/useGlobalLettaConfig';
2
2
  export { useAgentMessages } from './hooks/useAgentMessages/useAgentMessages';
3
3
  export { useAgentState } from './hooks/useAgentState/useAgentState';
4
+ export { useAgentPassages } from './hooks/useAgentPassages/useAgentPassages';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-react",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Letta's react library",
5
5
  "private": false,
6
6
  "main": "./index.js",
@@ -6,6 +6,7 @@ import { useGlobalLettaConfig } from '../useGlobalLettaConfig/useGlobalLettaConf
6
6
  import type { LocalMessagesState } from '../../types';
7
7
  import { useCachedState } from '../useCachedState/useCachedState';
8
8
  import { useLettaClient } from '../useLettaClient/useLettaClient';
9
+ import { LettaMessageUnion, MessageCreate } from '@letta-ai/letta-client/api';
9
10
 
10
11
  interface UseAgentOptions {
11
12
  client?: LettaClient.Options;
@@ -52,6 +53,17 @@ function dedupeMessages(
52
53
  }, []);
53
54
  }
54
55
 
56
+ function messageCreateToMessageUnion(
57
+ messages: MessageCreate[]
58
+ ): LettaMessageUnion[] {
59
+ return messages.map((message, idx) => ({
60
+ messageType: message.role === 'user' ? 'user_message' : 'system_message',
61
+ content: message.content,
62
+ date: new Date(),
63
+ id: `autogenerated_${Date.now()}_${idx}`,
64
+ }));
65
+ }
66
+
55
67
  interface SendMessagePayload {
56
68
  messages: LettaRequest['messages'];
57
69
  }
@@ -73,7 +85,7 @@ export function useAgentMessages(options: UseAgentOptions) {
73
85
  }
74
86
  );
75
87
 
76
- const hasInitialLoaded = useRef<boolean>(false);
88
+ const agentIdMessagesLoadedRef = useRef(false);
77
89
 
78
90
  const [isLoading, setIsLoading] = useState(true);
79
91
  const [isFetching, setIsFetching] = useState(false);
@@ -86,10 +98,24 @@ export function useAgentMessages(options: UseAgentOptions) {
86
98
  async function sendNonStreamedMessage(
87
99
  sendMessagePayload: SendMessagePayload
88
100
  ) {
101
+ let originalMessageState: LocalMessagesState = localMessages;
89
102
  try {
90
103
  setIsSending(true);
91
104
  setSendingError(null);
92
105
 
106
+ const newMessages = messageCreateToMessageUnion(
107
+ sendMessagePayload.messages
108
+ );
109
+
110
+ setLocalMessages((prevState) => {
111
+ originalMessageState = prevState;
112
+
113
+ return {
114
+ ...prevState,
115
+ messages: [...prevState.messages, ...newMessages],
116
+ };
117
+ });
118
+
93
119
  const response = await localClient.agents.messages.create(agentId, {
94
120
  ...sendMessagePayload,
95
121
  ...messageOptions,
@@ -97,23 +123,39 @@ export function useAgentMessages(options: UseAgentOptions) {
97
123
 
98
124
  setLocalMessages((prevState) => ({
99
125
  ...prevState,
100
- messages: [...response.messages, ...prevState.messages],
126
+ messages: [...prevState.messages, ...response.messages],
101
127
  }));
102
128
  } catch (e) {
129
+ setLocalMessages(originalMessageState);
103
130
  setSendingError(e);
104
131
  } finally {
105
132
  setIsSending(false);
106
133
  }
107
134
  },
108
- [localClient]
135
+ [localClient, localMessages]
109
136
  );
110
137
 
111
138
  const sendStreamedMessage = useCallback(
112
139
  async function sendStreamedMessage(sendMessagePayload: SendMessagePayload) {
140
+ let originalMessageState: LocalMessagesState = localMessages;
141
+
113
142
  try {
114
143
  setIsSending(true);
115
144
  setSendingError(null);
116
145
 
146
+ const newMessages = messageCreateToMessageUnion(
147
+ sendMessagePayload.messages
148
+ );
149
+
150
+ setLocalMessages((prevState) => {
151
+ originalMessageState = prevState;
152
+
153
+ return {
154
+ ...prevState,
155
+ messages: [...prevState.messages, ...newMessages],
156
+ };
157
+ });
158
+
117
159
  const response = await localClient.agents.messages.createStream(
118
160
  agentId,
119
161
  {
@@ -262,15 +304,24 @@ export function useAgentMessages(options: UseAgentOptions) {
262
304
 
263
305
  const sendMessage = useCallback(
264
306
  function sendMessage(payload: SendMessagePayload) {
307
+ const filteredPayload = {
308
+ ...payload,
309
+ messages: payload.messages.filter((e) => !!e.content),
310
+ };
311
+
312
+ if (filteredPayload.messages.length === 0) {
313
+ return;
314
+ }
315
+
265
316
  if (isSending) {
266
317
  return;
267
318
  }
268
319
 
269
320
  if (method === 'stream') {
270
- return sendStreamedMessage(payload);
321
+ return sendStreamedMessage(filteredPayload);
271
322
  }
272
323
 
273
- return sendNonStreamedMessage(payload);
324
+ return sendNonStreamedMessage(filteredPayload);
274
325
  },
275
326
  [method, isSending, sendStreamedMessage, sendNonStreamedMessage]
276
327
  );
@@ -320,15 +371,11 @@ export function useAgentMessages(options: UseAgentOptions) {
320
371
  }, [getMessages]);
321
372
 
322
373
  useEffect(() => {
323
- if (hasInitialLoaded.current) {
324
- return;
374
+ if (!agentIdMessagesLoadedRef.current) {
375
+ setIsLoading(true);
376
+ void getMessages();
377
+ agentIdMessagesLoadedRef.current = true;
325
378
  }
326
-
327
- hasInitialLoaded.current = true;
328
-
329
- setIsLoading(true);
330
-
331
- getMessages();
332
379
  }, []);
333
380
 
334
381
  return {
@@ -0,0 +1,51 @@
1
+ import { useLettaClient } from '../useLettaClient/useLettaClient';
2
+ import { useCachedState } from '../useCachedState/useCachedState';
3
+ import type { AgentState, Passage } from '@letta-ai/letta-client/api';
4
+ import type { LettaClient } from '@letta-ai/letta-client';
5
+ import { useCallback, useEffect, useRef, useState } from 'react';
6
+
7
+ interface UseAgentStateOptions {
8
+ client?: LettaClient.Options;
9
+ agentId: string;
10
+ }
11
+
12
+ export function useAgentPassages(options: UseAgentStateOptions) {
13
+ const { client, agentId } = options;
14
+ const localClient = useLettaClient(client);
15
+ const loadedAgentId = useRef<string | null>(null);
16
+
17
+ const [localState, setLocalState] = useCachedState<Passage[] | undefined>(
18
+ `agent-passages-${agentId}`,
19
+ undefined
20
+ );
21
+
22
+ const [isLoading, setIsLoading] = useState(true);
23
+ const [isLoadingError, setIsLoadingError] = useState<unknown | null>(null);
24
+
25
+ const getAgentPassages = useCallback(async () => {
26
+ try {
27
+ const state = await localClient.agents.passages.list(agentId);
28
+
29
+ setLocalState(state);
30
+ } catch (error) {
31
+ setIsLoadingError(error);
32
+ } finally {
33
+ setIsLoading(false);
34
+ }
35
+ }, [agentId, localClient, setLocalState]);
36
+
37
+ useEffect(() => {
38
+ if (agentId !== loadedAgentId.current) {
39
+ setIsLoading(true);
40
+ void getAgentPassages();
41
+ loadedAgentId.current = agentId;
42
+ }
43
+ }, [agentId, getAgentPassages]);
44
+
45
+ return {
46
+ isLoading,
47
+ isLoadingError,
48
+ passages: localState,
49
+ refresh: getAgentPassages,
50
+ };
51
+ }
@@ -1,7 +1,7 @@
1
1
  import { useLettaClient } from '../useLettaClient/useLettaClient';
2
2
  import type { LettaClient } from '@letta-ai/letta-client';
3
3
  import { useCachedState } from '../useCachedState/useCachedState';
4
- import type { AgentState } from '@letta-ai/letta-client/api';
4
+ import type { AgentState, UpdateAgent } from '@letta-ai/letta-client/api';
5
5
  import { useCallback, useEffect, useRef, useState } from 'react';
6
6
 
7
7
  interface UseAgentStateOptions {
@@ -19,8 +19,10 @@ export function useAgentState(options: UseAgentStateOptions) {
19
19
  );
20
20
 
21
21
  const [isLoading, setIsLoading] = useState(true);
22
+ const [isUpdating, setIsUpdating] = useState(false);
23
+ const [updatingError, setUpdatingError] = useState<unknown | null>(null);
22
24
  const [loadingError, setLoadingError] = useState<unknown | null>(null);
23
- const hasInitialLoaded = useRef<boolean>(false);
25
+ const loadedAgentId = useRef<string | null>(null);
24
26
 
25
27
  const getAgentState = useCallback(async () => {
26
28
  try {
@@ -34,21 +36,36 @@ export function useAgentState(options: UseAgentStateOptions) {
34
36
  }
35
37
  }, [agentId, localClient, setLocalState]);
36
38
 
37
- useEffect(() => {
38
- if (hasInitialLoaded.current) {
39
- return;
40
- }
41
-
42
- hasInitialLoaded.current = true;
39
+ const updateAgentState = useCallback(
40
+ async (state: Partial<UpdateAgent>) => {
41
+ setIsUpdating(true);
42
+ try {
43
+ const response = await localClient.agents.modify(agentId, state);
43
44
 
44
- setIsLoading(true);
45
+ setLocalState(response);
46
+ } catch (error) {
47
+ setUpdatingError(error);
48
+ } finally {
49
+ setIsUpdating(false);
50
+ }
51
+ },
52
+ [agentId, localClient, setLocalState]
53
+ );
45
54
 
46
- getAgentState();
47
- }, []);
55
+ useEffect(() => {
56
+ if (agentId !== loadedAgentId.current) {
57
+ setIsLoading(true);
58
+ void getAgentState();
59
+ loadedAgentId.current = agentId;
60
+ }
61
+ }, [agentId]);
48
62
 
49
63
  return {
50
64
  isLoading,
51
- error: loadingError,
65
+ updateAgentState,
66
+ isUpdating,
67
+ updatingError,
68
+ loadingError,
52
69
  agentState: localState,
53
70
  refresh: getAgentState,
54
71
  };
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { LettaProvider } from './hooks/useGlobalLettaConfig/useGlobalLettaConfig';
2
2
  export { useAgentMessages } from './hooks/useAgentMessages/useAgentMessages';
3
3
  export { useAgentState } from './hooks/useAgentState/useAgentState';
4
+ export { useAgentPassages } from './hooks/useAgentPassages/useAgentPassages';