Spaces:
Runtime error
Runtime error
File size: 8,914 Bytes
fbbb23b 0282efb ecd2209 0282efb 12a62f3 0282efb 34d0048 0282efb 4d36081 0282efb e1776b1 e0cbbfe 12a62f3 fbbb23b 12a62f3 e1776b1 a31142b 12a62f3 fbbb23b 0282efb e97538a de53269 0282efb e0cbbfe 12a62f3 0282efb e0cbbfe fbbb23b e0cbbfe 34d0048 e0cbbfe 0282efb e0cbbfe fbbb23b 0282efb fbbb23b e0cbbfe 0282efb a31142b e0cbbfe 0282efb e0cbbfe 0282efb fbbb23b 0282efb e0cbbfe 0282efb e0cbbfe 0282efb fbbb23b 0282efb e0cbbfe 0282efb e97538a fbbb23b 0282efb e0cbbfe 0282efb e0cbbfe e97538a 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb fbbb23b 0282efb e1776b1 e0cbbfe 43b7128 e0cbbfe 43b7128 fbbb23b 34d0048 e97538a 34d0048 e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb fbbb23b e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 2b0a7db 0282efb e0cbbfe 2b0a7db 0282efb e97538a 0282efb e0cbbfe 0282efb e0cbbfe 0282efb e0cbbfe 0282efb 65d2933 053c1a1 e97538a de53269 e97538a 65d2933 de53269 65d2933 e0cbbfe 65d2933 de53269 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
import uuid
import base64
import re
from io import BytesIO
from typing import Optional
from codeboxapi import CodeBox # type: ignore
from codeboxapi.schema import CodeBoxOutput # type: ignore
from langchain.tools import StructuredTool, BaseTool
from langchain.chat_models import ChatOpenAI
from langchain.chat_models.base import BaseChatModel
from langchain.prompts.chat import MessagesPlaceholder
from langchain.agents import AgentExecutor, BaseSingleActionAgent
from langchain.memory import ConversationBufferMemory
from codeinterpreterapi.schema import CodeInterpreterResponse, CodeInput, File, UserRequest
from codeinterpreterapi.config import settings
from codeinterpreterapi.chains.functions_agent import OpenAIFunctionsAgent
from codeinterpreterapi.prompts import code_interpreter_system_message
from codeinterpreterapi.callbacks import CodeCallbackHandler
from codeinterpreterapi.chains.modifications_check import get_file_modifications
from codeinterpreterapi.chains.remove_download_link import remove_download_link
class CodeInterpreterSession:
def __init__(
self,
model=None,
openai_api_key=settings.OPENAI_API_KEY,
openai_proxy=settings.OPENAI_PROXY,
openai_api_base=settings.OPENAI_API_BASE,
verbose=settings.VERBOSE,
tools: list[BaseTool] = None
) -> None:
self.codebox = CodeBox()
self.verbose = verbose
self.tools: list[BaseTool] = self._tools(tools)
self.llm: BaseChatModel = self._llm(
model, openai_api_key, openai_proxy, openai_api_base)
self.agent_executor: AgentExecutor = self._agent_executor()
self.input_files: list[File] = []
self.output_files: list[File] = []
async def astart(self) -> None:
await self.codebox.astart()
def _tools(self, additional_tools: list[BaseTool] = None) -> list[BaseTool]:
additional_tools = additional_tools or []
return additional_tools + [
StructuredTool(
name="python",
description= # TODO: variables as context to the agent
# TODO: current files as context to the agent
"Input a string of code to a python interpreter (jupyter kernel). "
"Variables are preserved between runs. ",
func=self.run_handler,
coroutine=self.arun_handler,
args_schema=CodeInput,
),
]
def _llm(self, model: Optional[str] = None, openai_api_key: Optional[str] = None, openai_proxy: Optional[str] = None, openai_api_base: Optional[str] = None) -> BaseChatModel:
print("OpenAI API Key:", openai_api_key)
print("OpenAI Proxy:", openai_proxy)
print("OpenAI API Base:", openai_api_base)
print("OpenAI Model:", model)
if model is None:
model = "gpt-3.5-turbo"
if openai_api_key is None:
raise ValueError(
"OpenAI API key missing. Set OPENAI_API_KEY env variable or pass `openai_api_key` to session."
)
return ChatOpenAI(
temperature=0.03,
model=model,
openai_api_key=openai_api_key,
# openai_proxy=openai_proxy,
openai_api_base=openai_api_base,
max_retries=3,
request_timeout=60 * 3,
) # type: ignore
def _agent(self) -> BaseSingleActionAgent:
return OpenAIFunctionsAgent.from_llm_and_tools(
llm=self.llm,
tools=self.tools,
system_message=code_interpreter_system_message,
extra_prompt_messages=[
MessagesPlaceholder(variable_name="memory")],
)
def _agent_executor(self) -> AgentExecutor:
return AgentExecutor.from_agent_and_tools(
agent=self._agent(),
callbacks=[CodeCallbackHandler(self)],
max_iterations=9,
tools=self.tools,
verbose=self.verbose,
memory=ConversationBufferMemory(
memory_key="memory", return_messages=True),
)
async def show_code(self, code: str) -> None:
"""Callback function to show code to the user."""
if self.verbose:
print(code)
def run_handler(self, code: str):
raise NotImplementedError("Use arun_handler for now.")
async def arun_handler(self, code: str):
"""Run code in container and send the output to the user"""
output: CodeBoxOutput = await self.codebox.arun(code)
if not isinstance(output.content, str):
raise TypeError("Expected output.content to be a string.")
if output.type == "image/png":
filename = f"image-{uuid.uuid4()}.png"
file_buffer = BytesIO(base64.b64decode(output.content))
file_buffer.name = filename
self.output_files.append(
File(name=filename, content=file_buffer.read()))
return f"Image {filename} got send to the user."
elif output.type == "error":
if "ModuleNotFoundError" in output.content:
if package := re.search(
r"ModuleNotFoundError: No module named '(.*)'", output.content
):
await self.codebox.ainstall(package.group(1))
return f"{package.group(1)} was missing but got installed now. Please try again."
else:
pass
# TODO: preanalyze error to optimize next code generation
if self.verbose:
print("Error:", output.content)
elif modifications := await get_file_modifications(code, self.llm):
for filename in modifications:
if filename in [file.name for file in self.input_files]:
continue
fileb = await self.codebox.adownload(filename)
if not fileb.content:
continue
file_buffer = BytesIO(fileb.content)
file_buffer.name = filename
self.output_files.append(
File(name=filename, content=file_buffer.read())
)
return output.content
async def input_handler(self, request: UserRequest):
if not request.files:
return
if not request.content:
request.content = (
"I uploaded, just text me back and confirm that you got the file(s)."
)
request.content += "\n**The user uploaded the following files: **\n"
for file in request.files:
self.input_files.append(file)
request.content += f"[Attachment: {file.name}]\n"
await self.codebox.aupload(file.name, file.content)
request.content += "**File(s) are now available in the cwd. **\n"
async def output_handler(self, final_response: str) -> CodeInterpreterResponse:
"""Embed images in the response"""
for file in self.output_files:
if str(file.name) in final_response:
# rm  from the response
final_response = re.sub(
rf"\n\n!\[.*\]\(.*\)", "", final_response)
if self.output_files and re.search(rf"\n\[.*\]\(.*\)", final_response):
final_response = await remove_download_link(final_response, self.llm)
return CodeInterpreterResponse(content=final_response, files=self.output_files)
async def generate_response(
self,
user_msg: str,
files: list[File] = [],
detailed_error: bool = False,
) -> CodeInterpreterResponse:
"""Generate a Code Interpreter response based on the user's input."""
user_request = UserRequest(content=user_msg, files=files)
try:
await self.input_handler(user_request)
response = await self.agent_executor.arun(input=user_request.content)
return await self.output_handler(response)
except Exception as e:
if self.verbose:
import traceback
traceback.print_exc()
if detailed_error:
return CodeInterpreterResponse(
content=f"Error in CodeInterpreterSession: {e.__class__.__name__} - {e}"
)
else:
return CodeInterpreterResponse(
content="Sorry, something went while generating your response."
"Please try again or restart the session."
)
async def is_running(self) -> bool:
return await self.codebox.astatus() == "running"
async def astop(self) -> None:
await self.codebox.astop()
async def __aenter__(self) -> "CodeInterpreterSession":
await self.astart()
return self
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
await self.astop()
|