1
1
import base64
2
2
import re
3
3
import traceback
4
- import uuid
5
4
from io import BytesIO
6
5
from os import getenv
7
6
from typing import Optional
7
+ from uuid import UUID , uuid4
8
8
9
9
from codeboxapi import CodeBox # type: ignore
10
10
from codeboxapi .schema import CodeBoxOutput # type: ignore
17
17
from langchain .chat_models import AzureChatOpenAI , ChatAnthropic , ChatOpenAI
18
18
from langchain .chat_models .base import BaseChatModel
19
19
from langchain .memory import ConversationBufferMemory
20
+ from langchain .memory .chat_message_histories import (
21
+ ChatMessageHistory ,
22
+ PostgresChatMessageHistory ,
23
+ RedisChatMessageHistory ,
24
+ )
20
25
from langchain .prompts .chat import MessagesPlaceholder
21
- from langchain .schema . language_model import BaseLanguageModel
26
+ from langchain .schema import BaseChatMessageHistory , BaseLanguageModel
22
27
from langchain .tools import BaseTool , StructuredTool
23
28
24
29
from codeinterpreterapi .agents import OpenAIFunctionsAgent
28
33
get_file_modifications ,
29
34
remove_download_link ,
30
35
)
36
+ from codeinterpreterapi .chat_history import CodeBoxChatMessageHistory
31
37
from codeinterpreterapi .config import settings
32
38
from codeinterpreterapi .parser import CodeAgentOutputParser , CodeChatAgentOutputParser
33
39
from codeinterpreterapi .prompts import code_interpreter_system_message
@@ -47,20 +53,35 @@ def __init__(
47
53
additional_tools : list [BaseTool ] = [],
48
54
** kwargs ,
49
55
) -> None :
50
- self .codebox = CodeBox (** kwargs )
56
+ self .codebox = CodeBox ()
51
57
self .verbose = kwargs .get ("verbose" , settings .VERBOSE )
52
58
self .tools : list [BaseTool ] = self ._tools (additional_tools )
53
59
self .llm : BaseLanguageModel = llm or self ._choose_llm (** kwargs )
54
- self .agent_executor : AgentExecutor = self . _agent_executor ()
60
+ self .agent_executor : Optional [ AgentExecutor ] = None
55
61
self .input_files : list [File ] = []
56
62
self .output_files : list [File ] = []
57
63
self .code_log : list [tuple [str , str ]] = []
58
64
65
+ @classmethod
66
+ def from_id (cls , session_id : UUID , ** kwargs ) -> "CodeInterpreterSession" :
67
+ session = cls (** kwargs )
68
+ session .codebox = CodeBox .from_id (session_id )
69
+ session .agent_executor = session ._agent_executor ()
70
+ return session
71
+
72
+ @property
73
+ def session_id (self ) -> Optional [UUID ]:
74
+ return self .codebox .session_id
75
+
59
76
def start (self ) -> SessionStatus :
60
- return SessionStatus .from_codebox_status (self .codebox .start ())
77
+ status = SessionStatus .from_codebox_status (self .codebox .start ())
78
+ self .agent_executor = self ._agent_executor ()
79
+ return status
61
80
62
81
async def astart (self ) -> SessionStatus :
63
- return SessionStatus .from_codebox_status (await self .codebox .astart ())
82
+ status = SessionStatus .from_codebox_status (await self .codebox .astart ())
83
+ self .agent_executor = self ._agent_executor ()
84
+ return status
64
85
65
86
def _tools (self , additional_tools : list [BaseTool ]) -> list [BaseTool ]:
66
87
return additional_tools + [
@@ -108,15 +129,15 @@ def _choose_llm(
108
129
openai_api_key = openai_api_key ,
109
130
max_retries = 3 ,
110
131
request_timeout = 60 * 3 ,
111
- )
132
+ ) # type: ignore
112
133
else :
113
134
return ChatOpenAI (
114
135
temperature = 0.03 ,
115
136
model = model ,
116
137
openai_api_key = openai_api_key ,
117
138
max_retries = 3 ,
118
139
request_timeout = 60 * 3 ,
119
- )
140
+ ) # type: ignore
120
141
elif "claude" in model :
121
142
return ChatAnthropic (model = model )
122
143
else :
@@ -148,14 +169,33 @@ def _choose_agent(self) -> BaseSingleActionAgent:
148
169
)
149
170
)
150
171
172
+ def _history_backend (self ) -> BaseChatMessageHistory :
173
+ return (
174
+ CodeBoxChatMessageHistory (codebox = self .codebox )
175
+ if settings .HISTORY_BACKEND == "codebox"
176
+ else RedisChatMessageHistory (
177
+ session_id = str (self .session_id ),
178
+ url = settings .REDIS_URL ,
179
+ )
180
+ if settings .HISTORY_BACKEND == "redis"
181
+ else PostgresChatMessageHistory (
182
+ session_id = str (self .session_id ),
183
+ connection_string = settings .POSTGRES_URL ,
184
+ )
185
+ if settings .HISTORY_BACKEND == "postgres"
186
+ else ChatMessageHistory ()
187
+ )
188
+
151
189
def _agent_executor (self ) -> AgentExecutor :
152
190
return AgentExecutor .from_agent_and_tools (
153
191
agent = self ._choose_agent (),
154
192
max_iterations = 9 ,
155
193
tools = self .tools ,
156
194
verbose = self .verbose ,
157
195
memory = ConversationBufferMemory (
158
- memory_key = "chat_history" , return_messages = True
196
+ memory_key = "chat_history" ,
197
+ return_messages = True ,
198
+ chat_memory = self ._history_backend (),
159
199
),
160
200
)
161
201
@@ -178,7 +218,7 @@ def _run_handler(self, code: str):
178
218
raise TypeError ("Expected output.content to be a string." )
179
219
180
220
if output .type == "image/png" :
181
- filename = f"image-{ uuid . uuid4 ()} .png"
221
+ filename = f"image-{ uuid4 ()} .png"
182
222
file_buffer = BytesIO (base64 .b64decode (output .content ))
183
223
file_buffer .name = filename
184
224
self .output_files .append (File (name = filename , content = file_buffer .read ()))
@@ -225,7 +265,7 @@ async def _arun_handler(self, code: str):
225
265
raise TypeError ("Expected output.content to be a string." )
226
266
227
267
if output .type == "image/png" :
228
- filename = f"image-{ uuid . uuid4 ()} .png"
268
+ filename = f"image-{ uuid4 ()} .png"
229
269
file_buffer = BytesIO (base64 .b64decode (output .content ))
230
270
file_buffer .name = filename
231
271
self .output_files .append (File (name = filename , content = file_buffer .read ()))
@@ -349,6 +389,7 @@ def generate_response_sync(
349
389
user_request = UserRequest (content = user_msg , files = files )
350
390
try :
351
391
self ._input_handler (user_request )
392
+ assert self .agent_executor , "Session not initialized."
352
393
response = self .agent_executor .run (input = user_request .content )
353
394
return self ._output_handler (response )
354
395
except Exception as e :
@@ -392,6 +433,7 @@ async def agenerate_response(
392
433
user_request = UserRequest (content = user_msg , files = files )
393
434
try :
394
435
await self ._ainput_handler (user_request )
436
+ assert self .agent_executor , "Session not initialized."
395
437
response = await self .agent_executor .arun (input = user_request .content )
396
438
return await self ._aoutput_handler (response )
397
439
except Exception as e :
0 commit comments