mirror of
https://github.com/corca-ai/EVAL
synced 2024-10-30 09:20:44 +00:00
Feature/async inference (#31)
* feat: async execution * feat: poll execution status * feat: disable submit button while executing * feat: execution tracing callback * feat: handle polling events on client side * fix: show last tool response * fix: tool index * fix: remove sidebar * fix: action input width * fix: hide input and observation on final answer * feat: stack actions * fix: run workers on serve * docs: update usage
This commit is contained in:
parent
880eba351e
commit
7123fe01b3
13
README.md
13
README.md
@ -111,13 +111,20 @@ Some tools requires environment variables. Set envs depend on which tools you wa
|
|||||||
|
|
||||||
### 3. Send request to EVAL
|
### 3. Send request to EVAL
|
||||||
|
|
||||||
|
- Use the Web GUI to use EVAL in ease
|
||||||
|
|
||||||
|
- Go to `http://localhost:8000` in your browser
|
||||||
|
<img src="assets/gui.png" />
|
||||||
|
|
||||||
|
- Or you can manually send request to EVAL with APIs.
|
||||||
|
|
||||||
- `POST /api/execute`
|
- `POST /api/execute`
|
||||||
|
|
||||||
- `session` - session id
|
- `session` - session id
|
||||||
- `files` - urls of file inputs
|
- `files` - urls of file inputs
|
||||||
- `prompt` - prompt
|
- `prompt` - prompt
|
||||||
|
|
||||||
- You can send request to EVAL with `curl` or `httpie`.
|
- examples
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST -H "Content-Type: application/json" -d '{"session": "sessionid", "files": [], "prompt": "Hi there!"}' http://localhost:8000/api/execute
|
curl -X POST -H "Content-Type: application/json" -d '{"session": "sessionid", "files": [], "prompt": "Hi there!"}' http://localhost:8000/api/execute
|
||||||
@ -127,7 +134,9 @@ Some tools requires environment variables. Set envs depend on which tools you wa
|
|||||||
http POST http://localhost:8000/api/execute session=sessionid files:='[]' prompt="Hi there!"
|
http POST http://localhost:8000/api/execute session=sessionid files:='[]' prompt="Hi there!"
|
||||||
```
|
```
|
||||||
|
|
||||||
- We are planning to make a GUI for EVAL so you can use it without terminal.
|
- It also supports asynchronous execution. You can use `POST /api/execute/async` instead of `POST /api/execute`, with same body.
|
||||||
|
|
||||||
|
- It returns `id` of the execution. Use `GET /api/execute/async/{id}` to get the result.
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
62
api/container.py
Normal file
62
api/container.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
|
from core.agents.manager import AgentManager
|
||||||
|
from core.handlers.base import BaseHandler, FileHandler, FileType
|
||||||
|
from core.handlers.dataframe import CsvToDataframe
|
||||||
|
from core.tools.base import BaseToolSet
|
||||||
|
from core.tools.cpu import ExitConversation, RequestsGet
|
||||||
|
from core.tools.editor import CodeEditor
|
||||||
|
from core.tools.terminal import Terminal
|
||||||
|
from core.upload import StaticUploader
|
||||||
|
from env import settings
|
||||||
|
|
||||||
|
BASE_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
os.chdir(BASE_DIR / settings["PLAYGROUND_DIR"])
|
||||||
|
|
||||||
|
|
||||||
|
toolsets: List[BaseToolSet] = [
|
||||||
|
Terminal(),
|
||||||
|
CodeEditor(),
|
||||||
|
RequestsGet(),
|
||||||
|
ExitConversation(),
|
||||||
|
]
|
||||||
|
handlers: Dict[FileType, BaseHandler] = {FileType.DATAFRAME: CsvToDataframe()}
|
||||||
|
|
||||||
|
if settings["USE_GPU"]:
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from core.handlers.image import ImageCaptioning
|
||||||
|
from core.tools.gpu import (
|
||||||
|
ImageEditing,
|
||||||
|
InstructPix2Pix,
|
||||||
|
Text2Image,
|
||||||
|
VisualQuestionAnswering,
|
||||||
|
)
|
||||||
|
|
||||||
|
if torch.cuda.is_available():
|
||||||
|
toolsets.extend(
|
||||||
|
[
|
||||||
|
Text2Image("cuda"),
|
||||||
|
ImageEditing("cuda"),
|
||||||
|
InstructPix2Pix("cuda"),
|
||||||
|
VisualQuestionAnswering("cuda"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
handlers[FileType.IMAGE] = ImageCaptioning("cuda")
|
||||||
|
|
||||||
|
agent_manager = AgentManager.create(toolsets=toolsets)
|
||||||
|
|
||||||
|
file_handler = FileHandler(handlers=handlers, path=BASE_DIR)
|
||||||
|
|
||||||
|
templates = Jinja2Templates(directory=BASE_DIR / "api" / "templates")
|
||||||
|
|
||||||
|
uploader = StaticUploader.from_settings(
|
||||||
|
settings, path=BASE_DIR / "static", endpoint="static"
|
||||||
|
)
|
||||||
|
|
||||||
|
reload_dirs = [BASE_DIR / "core", BASE_DIR / "api"]
|
102
api/main.py
102
api/main.py
@ -1,72 +1,22 @@
|
|||||||
import os
|
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from multiprocessing import Process
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from typing import Dict, List, TypedDict
|
from typing import List, TypedDict
|
||||||
|
|
||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI, Request, UploadFile
|
from fastapi import FastAPI, Request, UploadFile
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.templating import Jinja2Templates
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from core.agents.manager import AgentManager
|
from api.container import agent_manager, file_handler, reload_dirs, templates, uploader
|
||||||
from core.handlers.base import BaseHandler, FileHandler, FileType
|
from api.worker import get_task_result, start_worker, task_execute
|
||||||
from core.handlers.dataframe import CsvToDataframe
|
|
||||||
from core.tools.base import BaseToolSet
|
|
||||||
from core.tools.cpu import ExitConversation, RequestsGet
|
|
||||||
from core.tools.editor import CodeEditor
|
|
||||||
from core.tools.terminal import Terminal
|
|
||||||
from core.upload import StaticUploader
|
|
||||||
from env import settings
|
from env import settings
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
BASE_DIR = Path(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
||||||
os.chdir(BASE_DIR / settings["PLAYGROUND_DIR"])
|
|
||||||
|
|
||||||
uploader = StaticUploader.from_settings(
|
|
||||||
settings, path=BASE_DIR / "static", endpoint="static"
|
|
||||||
)
|
|
||||||
app.mount("/static", StaticFiles(directory=uploader.path), name="static")
|
app.mount("/static", StaticFiles(directory=uploader.path), name="static")
|
||||||
|
|
||||||
templates = Jinja2Templates(directory=BASE_DIR / "api" / "templates")
|
|
||||||
|
|
||||||
toolsets: List[BaseToolSet] = [
|
|
||||||
Terminal(),
|
|
||||||
CodeEditor(),
|
|
||||||
RequestsGet(),
|
|
||||||
ExitConversation(),
|
|
||||||
]
|
|
||||||
handlers: Dict[FileType, BaseHandler] = {FileType.DATAFRAME: CsvToDataframe()}
|
|
||||||
|
|
||||||
if settings["USE_GPU"]:
|
|
||||||
import torch
|
|
||||||
|
|
||||||
from core.handlers.image import ImageCaptioning
|
|
||||||
from core.tools.gpu import (
|
|
||||||
ImageEditing,
|
|
||||||
InstructPix2Pix,
|
|
||||||
Text2Image,
|
|
||||||
VisualQuestionAnswering,
|
|
||||||
)
|
|
||||||
|
|
||||||
if torch.cuda.is_available():
|
|
||||||
toolsets.extend(
|
|
||||||
[
|
|
||||||
Text2Image("cuda"),
|
|
||||||
ImageEditing("cuda"),
|
|
||||||
InstructPix2Pix("cuda"),
|
|
||||||
VisualQuestionAnswering("cuda"),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
handlers[FileType.IMAGE] = ImageCaptioning("cuda")
|
|
||||||
|
|
||||||
agent_manager = AgentManager.create(toolsets=toolsets)
|
|
||||||
file_handler = FileHandler(handlers=handlers, path=BASE_DIR)
|
|
||||||
|
|
||||||
|
|
||||||
class ExecuteRequest(BaseModel):
|
class ExecuteRequest(BaseModel):
|
||||||
session: str
|
session: str
|
||||||
@ -107,7 +57,7 @@ async def execute(request: ExecuteRequest) -> ExecuteResponse:
|
|||||||
files = request.files
|
files = request.files
|
||||||
session = request.session
|
session = request.session
|
||||||
|
|
||||||
executor = agent_manager.get_or_create_executor(session)
|
executor = agent_manager.create_executor(session)
|
||||||
|
|
||||||
promptedQuery = "\n".join([file_handler.handle(file) for file in files])
|
promptedQuery = "\n".join([file_handler.handle(file) for file in files])
|
||||||
promptedQuery += query
|
promptedQuery += query
|
||||||
@ -117,7 +67,7 @@ async def execute(request: ExecuteRequest) -> ExecuteResponse:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"answer": str(e), "files": []}
|
return {"answer": str(e), "files": []}
|
||||||
|
|
||||||
files = re.findall(r"\[file/\S*\]", res["output"])
|
files = re.findall(r"\[file://\S*\]", res["output"])
|
||||||
files = [file[1:-1] for file in files]
|
files = [file[1:-1] for file in files]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -126,15 +76,53 @@ async def execute(request: ExecuteRequest) -> ExecuteResponse:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/execute/async")
|
||||||
|
async def execute_async(request: ExecuteRequest):
|
||||||
|
query = request.prompt
|
||||||
|
files = request.files
|
||||||
|
session = request.session
|
||||||
|
|
||||||
|
promptedQuery = "\n".join([file_handler.handle(file) for file in files])
|
||||||
|
promptedQuery += query
|
||||||
|
|
||||||
|
execution = task_execute.delay(session, promptedQuery)
|
||||||
|
return {"id": execution.id}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/execute/async/{execution_id}")
|
||||||
|
async def execute_async(execution_id: str):
|
||||||
|
execution = get_task_result(execution_id)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
if execution.status == "SUCCESS" and execution.result:
|
||||||
|
output = execution.result.get("output", "")
|
||||||
|
files = re.findall(r"\[file://\S*\]", output)
|
||||||
|
files = [file[1:-1] for file in files]
|
||||||
|
result = {
|
||||||
|
"answer": output,
|
||||||
|
"files": [uploader.upload(file) for file in files],
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": execution.status,
|
||||||
|
"info": execution.info,
|
||||||
|
"result": result,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def serve():
|
def serve():
|
||||||
|
p = Process(target=start_worker, args=[])
|
||||||
|
p.start()
|
||||||
uvicorn.run("api.main:app", host="0.0.0.0", port=settings["EVAL_PORT"])
|
uvicorn.run("api.main:app", host="0.0.0.0", port=settings["EVAL_PORT"])
|
||||||
|
|
||||||
|
|
||||||
def dev():
|
def dev():
|
||||||
|
p = Process(target=start_worker, args=[])
|
||||||
|
p.start()
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
"api.main:app",
|
"api.main:app",
|
||||||
host="0.0.0.0",
|
host="0.0.0.0",
|
||||||
port=settings["EVAL_PORT"],
|
port=settings["EVAL_PORT"],
|
||||||
reload=True,
|
reload=True,
|
||||||
reload_dirs=[BASE_DIR / "core", BASE_DIR / "api"],
|
reload_dirs=reload_dirs,
|
||||||
)
|
)
|
||||||
|
@ -44,9 +44,9 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</header>
|
</header>
|
||||||
<div class="d-flex flex-row">
|
<div class="d-flex flex-row">
|
||||||
<div
|
<!-- <div
|
||||||
class="d-flex flex-column flex-shrink-0 p-3 bg-body-tertiary"
|
class="d-flex flex-column flex-shrink-0 p-3 bg-body-tertiary"
|
||||||
style="width: 280px; height: 80vh"
|
style="width: 240px; height: 80vh"
|
||||||
>
|
>
|
||||||
<ul id="nav-sidebar" class="nav nav-pills flex-column mb-auto">
|
<ul id="nav-sidebar" class="nav nav-pills flex-column mb-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
@ -67,7 +67,7 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
<div class="container">{% block content %}{% endblock %}</div>
|
<div class="container">{% block content %}{% endblock %}</div>
|
||||||
|
@ -17,15 +17,32 @@
|
|||||||
<label for="session" class="form-label">Session</label>
|
<label for="session" class="form-label">Session</label>
|
||||||
<input id="session" name="session" class="form-control" />
|
<input id="session" name="session" class="form-control" />
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary" onclick="submit(event)">
|
<button
|
||||||
|
id="submit"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-primary"
|
||||||
|
onclick="submit(event)"
|
||||||
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
id="submit-loader"
|
||||||
|
class="btn btn-primary disabled"
|
||||||
|
style="display: none"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
<div class="spinner-border spinner-border-sm"></div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-body-tertiary border rounded-3 p-2">
|
||||||
|
<div id="actions"></div>
|
||||||
|
<div class="card m-2">
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="answer" class="card-text"></div>
|
||||||
|
<div id="response-files" class="card-text"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-body-tertiary border rounded-3 p-3">
|
|
||||||
<div>
|
|
||||||
<div id="answer"></div>
|
|
||||||
<div id="response-files"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
42
api/worker.py
Normal file
42
api/worker.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from celery import Celery
|
||||||
|
from celery.result import AsyncResult
|
||||||
|
|
||||||
|
from api.container import agent_manager
|
||||||
|
from env import settings
|
||||||
|
|
||||||
|
celery_app = Celery(__name__)
|
||||||
|
celery_app.conf.broker_url = settings["CELERY_BROKER_URL"]
|
||||||
|
celery_app.conf.result_backend = settings["CELERY_BROKER_URL"]
|
||||||
|
celery_app.conf.update(
|
||||||
|
task_track_started=True,
|
||||||
|
task_serializer="json",
|
||||||
|
accept_content=["json"], # Ignore other content
|
||||||
|
result_serializer="json",
|
||||||
|
enable_utc=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@celery_app.task(name="task_execute", bind=True)
|
||||||
|
def task_execute(self, session: str, prompt: str):
|
||||||
|
executor = agent_manager.create_executor(session, self)
|
||||||
|
response = executor({"input": prompt})
|
||||||
|
result = {"output": response["output"]}
|
||||||
|
|
||||||
|
previous = AsyncResult(self.request.id)
|
||||||
|
if previous and previous.info:
|
||||||
|
result.update(previous.info)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def get_task_result(task_id):
|
||||||
|
return AsyncResult(task_id)
|
||||||
|
|
||||||
|
|
||||||
|
def start_worker():
|
||||||
|
celery_app.worker_main(
|
||||||
|
[
|
||||||
|
"worker",
|
||||||
|
"--loglevel=INFO",
|
||||||
|
]
|
||||||
|
)
|
BIN
assets/gui.png
Normal file
BIN
assets/gui.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 302 KiB |
@ -4,6 +4,7 @@ from core.tools.factory import ToolsFactory
|
|||||||
from env import settings
|
from env import settings
|
||||||
from langchain.chat_models.base import BaseChatModel
|
from langchain.chat_models.base import BaseChatModel
|
||||||
from langchain.output_parsers.base import BaseOutputParser
|
from langchain.output_parsers.base import BaseOutputParser
|
||||||
|
from langchain.callbacks.base import BaseCallbackManager
|
||||||
|
|
||||||
from .chat_agent import ConversationalChatAgent
|
from .chat_agent import ConversationalChatAgent
|
||||||
from .llm import ChatOpenAI
|
from .llm import ChatOpenAI
|
||||||
@ -17,8 +18,10 @@ class AgentBuilder:
|
|||||||
self.global_tools: list = None
|
self.global_tools: list = None
|
||||||
self.toolsets = toolsets
|
self.toolsets = toolsets
|
||||||
|
|
||||||
def build_llm(self):
|
def build_llm(self, callback_manager: BaseCallbackManager = None):
|
||||||
self.llm = ChatOpenAI(temperature=0)
|
self.llm = ChatOpenAI(
|
||||||
|
temperature=0, callback_manager=callback_manager, verbose=True
|
||||||
|
)
|
||||||
self.llm.check_access()
|
self.llm.check_access()
|
||||||
|
|
||||||
def build_parser(self):
|
def build_parser(self):
|
||||||
@ -40,6 +43,12 @@ class AgentBuilder:
|
|||||||
*ToolsFactory.create_global_tools(self.toolsets),
|
*ToolsFactory.create_global_tools(self.toolsets),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_parser(self):
|
||||||
|
if self.parser is None:
|
||||||
|
raise ValueError("Parser is not initialized yet")
|
||||||
|
|
||||||
|
return self.parser
|
||||||
|
|
||||||
def get_global_tools(self):
|
def get_global_tools(self):
|
||||||
if self.global_tools is None:
|
if self.global_tools is None:
|
||||||
raise ValueError("Global tools are not initialized yet")
|
raise ValueError("Global tools are not initialized yet")
|
||||||
|
@ -2,22 +2,45 @@ from typing import Any, Dict, List, Optional, Union
|
|||||||
|
|
||||||
from langchain.callbacks.base import BaseCallbackHandler
|
from langchain.callbacks.base import BaseCallbackHandler
|
||||||
from langchain.schema import AgentAction, AgentFinish, LLMResult
|
from langchain.schema import AgentAction, AgentFinish, LLMResult
|
||||||
|
from celery import Task
|
||||||
|
|
||||||
from ansi import ANSI, Color, Style, dim_multiline
|
from ansi import ANSI, Color, Style, dim_multiline
|
||||||
from logger import logger
|
from logger import logger
|
||||||
|
|
||||||
|
|
||||||
class EVALCallbackHandler(BaseCallbackHandler):
|
class EVALCallbackHandler(BaseCallbackHandler):
|
||||||
|
@property
|
||||||
|
def ignore_llm(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def set_parser(self, parser) -> None:
|
||||||
|
self.parser = parser
|
||||||
|
|
||||||
def on_llm_start(
|
def on_llm_start(
|
||||||
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
|
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
||||||
pass
|
text = response.generations[0][0].text
|
||||||
|
|
||||||
|
parsed = self.parser.parse_all(text)
|
||||||
|
|
||||||
|
logger.info(ANSI("Plan").to(Color.blue().bright()) + ": " + parsed["plan"])
|
||||||
|
logger.info(ANSI("What I Did").to(Color.blue()) + ": " + parsed["what_i_did"])
|
||||||
|
logger.info(
|
||||||
|
ANSI("Action").to(Color.cyan())
|
||||||
|
+ ": "
|
||||||
|
+ ANSI(parsed["action"]).to(Style.bold())
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
ANSI("Input").to(Color.cyan())
|
||||||
|
+ ": "
|
||||||
|
+ dim_multiline(parsed["action_input"])
|
||||||
|
)
|
||||||
|
|
||||||
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
||||||
pass
|
logger.info(ANSI(f"on_llm_new_token {token}").to(Color.green(), Style.italic()))
|
||||||
|
|
||||||
def on_llm_error(
|
def on_llm_error(
|
||||||
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
@ -85,3 +108,90 @@ class EVALCallbackHandler(BaseCallbackHandler):
|
|||||||
+ ": "
|
+ ": "
|
||||||
+ dim_multiline(finish.return_values.get("output", ""))
|
+ dim_multiline(finish.return_values.get("output", ""))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExecutionTracingCallbackHandler(BaseCallbackHandler):
|
||||||
|
def __init__(self, execution: Task):
|
||||||
|
self.execution = execution
|
||||||
|
self.index = 0
|
||||||
|
|
||||||
|
def set_parser(self, parser) -> None:
|
||||||
|
self.parser = parser
|
||||||
|
|
||||||
|
def on_llm_start(
|
||||||
|
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
|
||||||
|
text = response.generations[0][0].text
|
||||||
|
parsed = self.parser.parse_all(text)
|
||||||
|
self.index += 1
|
||||||
|
parsed["index"] = self.index
|
||||||
|
self.execution.update_state(state="LLM_END", meta=parsed)
|
||||||
|
|
||||||
|
def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_llm_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_chain_start(
|
||||||
|
self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs: Any
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_chain_end(self, outputs: Dict[str, Any], **kwargs: Any) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_chain_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> None:
|
||||||
|
self.execution.update_state(state="CHAIN_ERROR", meta={"error": str(error)})
|
||||||
|
|
||||||
|
def on_tool_start(
|
||||||
|
self,
|
||||||
|
serialized: Dict[str, Any],
|
||||||
|
input_str: str,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_agent_action(self, action: AgentAction, **kwargs: Any) -> Any:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_tool_end(
|
||||||
|
self,
|
||||||
|
output: str,
|
||||||
|
observation_prefix: Optional[str] = None,
|
||||||
|
llm_prefix: Optional[str] = None,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
previous = self.execution.AsyncResult(self.execution.request.id)
|
||||||
|
self.execution.update_state(
|
||||||
|
state="TOOL_END", meta={**previous.info, "observation": output}
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_tool_error(
|
||||||
|
self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any
|
||||||
|
) -> None:
|
||||||
|
previous = self.execution.AsyncResult(self.execution.request.id)
|
||||||
|
self.execution.update_state(
|
||||||
|
state="TOOL_ERROR", meta={**previous.info, "error": str(error)}
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_text(
|
||||||
|
self,
|
||||||
|
text: str,
|
||||||
|
color: Optional[str] = None,
|
||||||
|
end: str = "",
|
||||||
|
**kwargs: Optional[str],
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_agent_finish(
|
||||||
|
self, finish: AgentFinish, color: Optional[str] = None, **kwargs: Any
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
from typing import Dict
|
from typing import Dict, Optional
|
||||||
|
from celery import Task
|
||||||
|
|
||||||
from langchain.agents.agent import Agent, AgentExecutor
|
from langchain.agents.agent import AgentExecutor
|
||||||
from langchain.agents.tools import BaseTool
|
|
||||||
from langchain.callbacks import set_handler
|
|
||||||
from langchain.callbacks.base import CallbackManager
|
from langchain.callbacks.base import CallbackManager
|
||||||
|
from langchain.callbacks import set_handler
|
||||||
from langchain.chains.conversation.memory import ConversationBufferMemory
|
from langchain.chains.conversation.memory import ConversationBufferMemory
|
||||||
from langchain.memory.chat_memory import BaseChatMemory
|
from langchain.memory.chat_memory import BaseChatMemory
|
||||||
|
|
||||||
@ -11,68 +11,73 @@ from core.tools.base import BaseToolSet
|
|||||||
from core.tools.factory import ToolsFactory
|
from core.tools.factory import ToolsFactory
|
||||||
|
|
||||||
from .builder import AgentBuilder
|
from .builder import AgentBuilder
|
||||||
from .callback import EVALCallbackHandler
|
from .callback import EVALCallbackHandler, ExecutionTracingCallbackHandler
|
||||||
|
|
||||||
|
|
||||||
callback_manager = CallbackManager([EVALCallbackHandler()])
|
|
||||||
set_handler(EVALCallbackHandler())
|
set_handler(EVALCallbackHandler())
|
||||||
|
|
||||||
|
|
||||||
class AgentManager:
|
class AgentManager:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
agent: Agent,
|
|
||||||
global_tools: list[BaseTool],
|
|
||||||
toolsets: list[BaseToolSet] = [],
|
toolsets: list[BaseToolSet] = [],
|
||||||
):
|
):
|
||||||
self.agent: Agent = agent
|
|
||||||
self.global_tools: list[BaseTool] = global_tools
|
|
||||||
self.toolsets: list[BaseToolSet] = toolsets
|
self.toolsets: list[BaseToolSet] = toolsets
|
||||||
|
self.memories: Dict[str, BaseChatMemory] = {}
|
||||||
self.executors: Dict[str, AgentExecutor] = {}
|
self.executors: Dict[str, AgentExecutor] = {}
|
||||||
|
|
||||||
def create_memory(self) -> BaseChatMemory:
|
def create_memory(self) -> BaseChatMemory:
|
||||||
return ConversationBufferMemory(memory_key="chat_history", return_messages=True)
|
return ConversationBufferMemory(memory_key="chat_history", return_messages=True)
|
||||||
|
|
||||||
def create_executor(self, session: str) -> AgentExecutor:
|
def get_or_create_memory(self, session: str) -> BaseChatMemory:
|
||||||
memory: BaseChatMemory = self.create_memory()
|
if not (session in self.memories):
|
||||||
|
self.memories[session] = self.create_memory()
|
||||||
|
return self.memories[session]
|
||||||
|
|
||||||
|
def create_executor(
|
||||||
|
self, session: str, execution: Optional[Task] = None
|
||||||
|
) -> AgentExecutor:
|
||||||
|
builder = AgentBuilder(self.toolsets)
|
||||||
|
builder.build_parser()
|
||||||
|
|
||||||
|
callbacks = []
|
||||||
|
eval_callback = EVALCallbackHandler()
|
||||||
|
eval_callback.set_parser(builder.get_parser())
|
||||||
|
callbacks.append(eval_callback)
|
||||||
|
if execution:
|
||||||
|
execution_callback = ExecutionTracingCallbackHandler(execution)
|
||||||
|
execution_callback.set_parser(builder.get_parser())
|
||||||
|
callbacks.append(execution_callback)
|
||||||
|
|
||||||
|
callback_manager = CallbackManager(callbacks)
|
||||||
|
|
||||||
|
builder.build_llm(callback_manager)
|
||||||
|
builder.build_global_tools()
|
||||||
|
|
||||||
|
memory: BaseChatMemory = self.get_or_create_memory(session)
|
||||||
tools = [
|
tools = [
|
||||||
*self.global_tools,
|
*builder.get_global_tools(),
|
||||||
*ToolsFactory.create_per_session_tools(
|
*ToolsFactory.create_per_session_tools(
|
||||||
self.toolsets,
|
self.toolsets,
|
||||||
get_session=lambda: (session, self.executors[session]),
|
get_session=lambda: (session, self.executors[session]),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
for tool in tools:
|
|
||||||
tool.set_callback_manager(callback_manager)
|
|
||||||
|
|
||||||
return AgentExecutor.from_agent_and_tools(
|
for tool in tools:
|
||||||
agent=self.agent,
|
tool.callback_manager = callback_manager
|
||||||
|
|
||||||
|
executor = AgentExecutor.from_agent_and_tools(
|
||||||
|
agent=builder.get_agent(),
|
||||||
tools=tools,
|
tools=tools,
|
||||||
memory=memory,
|
memory=memory,
|
||||||
callback_manager=callback_manager,
|
callback_manager=callback_manager,
|
||||||
verbose=True,
|
verbose=True,
|
||||||
)
|
)
|
||||||
|
self.executors[session] = executor
|
||||||
def remove_executor(self, session: str) -> None:
|
return executor
|
||||||
if session in self.executors:
|
|
||||||
del self.executors[session]
|
|
||||||
|
|
||||||
def get_or_create_executor(self, session: str) -> AgentExecutor:
|
|
||||||
if not (session in self.executors):
|
|
||||||
self.executors[session] = self.create_executor(session=session)
|
|
||||||
return self.executors[session]
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(toolsets: list[BaseToolSet]) -> "AgentManager":
|
def create(toolsets: list[BaseToolSet]) -> "AgentManager":
|
||||||
builder = AgentBuilder(toolsets)
|
|
||||||
builder.build_llm()
|
|
||||||
builder.build_parser()
|
|
||||||
builder.build_global_tools()
|
|
||||||
|
|
||||||
agent = builder.get_agent()
|
|
||||||
global_tools = builder.get_global_tools()
|
|
||||||
|
|
||||||
return AgentManager(
|
return AgentManager(
|
||||||
agent=agent,
|
|
||||||
global_tools=global_tools,
|
|
||||||
toolsets=toolsets,
|
toolsets=toolsets,
|
||||||
)
|
)
|
||||||
|
@ -1,16 +1,31 @@
|
|||||||
import re
|
import re
|
||||||
import time
|
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
from langchain.output_parsers.base import BaseOutputParser
|
from langchain.output_parsers.base import BaseOutputParser
|
||||||
|
|
||||||
from ansi import ANSI, Color, Style
|
|
||||||
from core.agents.callback import dim_multiline
|
|
||||||
from core.prompts.input import EVAL_FORMAT_INSTRUCTIONS
|
from core.prompts.input import EVAL_FORMAT_INSTRUCTIONS
|
||||||
from logger import logger
|
|
||||||
|
|
||||||
|
|
||||||
class EvalOutputParser(BaseOutputParser):
|
class EvalOutputParser(BaseOutputParser):
|
||||||
|
@staticmethod
|
||||||
|
def parse_all(text: str) -> Dict[str, str]:
|
||||||
|
regex = r"Action: (.*?)[\n]Plan:(.*)[\n]What I Did:(.*)[\n]Action Input: (.*)"
|
||||||
|
match = re.search(regex, text, re.DOTALL)
|
||||||
|
if not match:
|
||||||
|
raise Exception("parse error")
|
||||||
|
|
||||||
|
action = match.group(1).strip()
|
||||||
|
plan = match.group(2)
|
||||||
|
what_i_did = match.group(3)
|
||||||
|
action_input = match.group(4).strip(" ").strip('"')
|
||||||
|
|
||||||
|
return {
|
||||||
|
"action": action,
|
||||||
|
"plan": plan,
|
||||||
|
"what_i_did": what_i_did,
|
||||||
|
"action_input": action_input,
|
||||||
|
}
|
||||||
|
|
||||||
def get_format_instructions(self) -> str:
|
def get_format_instructions(self) -> str:
|
||||||
return EVAL_FORMAT_INSTRUCTIONS
|
return EVAL_FORMAT_INSTRUCTIONS
|
||||||
|
|
||||||
@ -20,21 +35,9 @@ class EvalOutputParser(BaseOutputParser):
|
|||||||
if not match:
|
if not match:
|
||||||
raise Exception("parse error")
|
raise Exception("parse error")
|
||||||
|
|
||||||
action = match.group(1).strip()
|
parsed = EvalOutputParser.parse_all(text)
|
||||||
plan = match.group(2)
|
|
||||||
what_i_did = match.group(3)
|
|
||||||
action_input = match.group(4)
|
|
||||||
|
|
||||||
logger.info(ANSI("Plan").to(Color.blue().bright()) + ": " + plan)
|
return {"action": parsed["action"], "action_input": parsed["action_input"]}
|
||||||
logger.info(ANSI("What I Did").to(Color.blue()) + ": " + what_i_did)
|
|
||||||
time.sleep(1)
|
|
||||||
logger.info(
|
|
||||||
ANSI("Action").to(Color.cyan()) + ": " + ANSI(action).to(Style.bold())
|
|
||||||
)
|
|
||||||
time.sleep(1)
|
|
||||||
logger.info(ANSI("Input").to(Color.cyan()) + ": " + dim_multiline(action_input))
|
|
||||||
time.sleep(1)
|
|
||||||
return {"action": action, "action_input": action_input.strip(" ").strip('"')}
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "EvalOutputParser"
|
return "EvalOutputParser"
|
||||||
|
@ -3,7 +3,7 @@ EVAL_PREFIX = """{bot_name} can execute any user's request.
|
|||||||
{bot_name} has permission to handle one instance and can handle the environment in it at will.
|
{bot_name} has permission to handle one instance and can handle the environment in it at will.
|
||||||
You can code, run, debug, and test yourself. You can correct the code appropriately by looking at the error message.
|
You can code, run, debug, and test yourself. You can correct the code appropriately by looking at the error message.
|
||||||
|
|
||||||
I can understand, process, and create various types of files. Every image, dataframe, audio, video must be restored in file/ directory.
|
I can understand, process, and create various types of files.
|
||||||
{bot_name} can do whatever it takes to execute the user's request. Let's think step by step.
|
{bot_name} can do whatever it takes to execute the user's request. Let's think step by step.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ EVAL_SUFFIX = """TOOLS
|
|||||||
{bot_name} can ask the user to use tools to look up information that may be helpful in answering the users original question.
|
{bot_name} can ask the user to use tools to look up information that may be helpful in answering the users original question.
|
||||||
You are very strict to the filename correctness and will never fake a file name if it does not exist.
|
You are very strict to the filename correctness and will never fake a file name if it does not exist.
|
||||||
You will remember to provide the file name loyally if it's provided in the last tool observation.
|
You will remember to provide the file name loyally if it's provided in the last tool observation.
|
||||||
If you have to include files in your response, you must move the files into file/ directory and provide the filename in [file/FILENAME] format. It must be wrapped in square brackets.
|
If you have to include files in your response, you must provide the filepath in [file://filepath] format. It must be wrapped in square brackets.
|
||||||
|
|
||||||
The tools the human can use are:
|
The tools the human can use are:
|
||||||
|
|
||||||
|
@ -16,6 +16,10 @@ services:
|
|||||||
- "8000:8000" # eval port
|
- "8000:8000" # eval port
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
|
environment:
|
||||||
|
- CELERY_BROKER_URL=redis://redis:6379
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
eval.gpu:
|
eval.gpu:
|
||||||
container_name: eval.gpu
|
container_name: eval.gpu
|
||||||
@ -40,3 +44,8 @@ services:
|
|||||||
- driver: nvidia
|
- driver: nvidia
|
||||||
device_ids: ["1"] # You can choose which GPU to use
|
device_ids: ["1"] # You can choose which GPU to use
|
||||||
capabilities: [gpu]
|
capabilities: [gpu]
|
||||||
|
depends_on:
|
||||||
|
- redis
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: redis:alpine
|
||||||
|
2
env.py
2
env.py
@ -12,6 +12,7 @@ class DotEnv(TypedDict):
|
|||||||
EVAL_PORT: int
|
EVAL_PORT: int
|
||||||
SERVER: str
|
SERVER: str
|
||||||
|
|
||||||
|
CELERY_BROKER_URL: str
|
||||||
USE_GPU: bool # optional
|
USE_GPU: bool # optional
|
||||||
PLAYGROUND_DIR: str # optional
|
PLAYGROUND_DIR: str # optional
|
||||||
LOG_LEVEL: str # optional
|
LOG_LEVEL: str # optional
|
||||||
@ -31,6 +32,7 @@ EVAL_PORT = int(os.getenv("EVAL_PORT", 8000))
|
|||||||
settings: DotEnv = {
|
settings: DotEnv = {
|
||||||
"EVAL_PORT": EVAL_PORT,
|
"EVAL_PORT": EVAL_PORT,
|
||||||
"MODEL_NAME": os.getenv("MODEL_NAME", "gpt-4"),
|
"MODEL_NAME": os.getenv("MODEL_NAME", "gpt-4"),
|
||||||
|
"CELERY_BROKER_URL": os.getenv("CELERY_BROKER_URL", "redis://localhost:6379"),
|
||||||
"SERVER": os.getenv("SERVER", f"http://localhost:{EVAL_PORT}"),
|
"SERVER": os.getenv("SERVER", f"http://localhost:{EVAL_PORT}"),
|
||||||
"USE_GPU": os.getenv("USE_GPU", "False").lower() == "true",
|
"USE_GPU": os.getenv("USE_GPU", "False").lower() == "true",
|
||||||
"PLAYGROUND_DIR": os.getenv("PLAYGROUND_DIR", "playground"),
|
"PLAYGROUND_DIR": os.getenv("PLAYGROUND_DIR", "playground"),
|
||||||
|
235
poetry.lock
generated
235
poetry.lock
generated
@ -1,4 +1,4 @@
|
|||||||
# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand.
|
# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand.
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "accelerate"
|
name = "accelerate"
|
||||||
@ -153,6 +153,21 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
frozenlist = ">=1.1.0"
|
frozenlist = ">=1.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "amqp"
|
||||||
|
version = "5.1.1"
|
||||||
|
description = "Low-level AMQP client for Python (fork of amqplib)."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"},
|
||||||
|
{file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
vine = ">=5.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyio"
|
name = "anyio"
|
||||||
version = "3.6.2"
|
version = "3.6.2"
|
||||||
@ -224,6 +239,18 @@ soupsieve = ">1.2"
|
|||||||
html5lib = ["html5lib"]
|
html5lib = ["html5lib"]
|
||||||
lxml = ["lxml"]
|
lxml = ["lxml"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "billiard"
|
||||||
|
version = "3.6.4.0"
|
||||||
|
description = "Python multiprocessing fork with improvements and bugfixes"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"},
|
||||||
|
{file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitsandbytes"
|
name = "bitsandbytes"
|
||||||
version = "0.37.2"
|
version = "0.37.2"
|
||||||
@ -325,6 +352,61 @@ urllib3 = ">=1.25.4,<1.27"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
crt = ["awscrt (==0.16.9)"]
|
crt = ["awscrt (==0.16.9)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "celery"
|
||||||
|
version = "5.2.7"
|
||||||
|
description = "Distributed Task Queue."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"},
|
||||||
|
{file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
billiard = ">=3.6.4.0,<4.0"
|
||||||
|
click = ">=8.0.3,<9.0"
|
||||||
|
click-didyoumean = ">=0.0.3"
|
||||||
|
click-plugins = ">=1.1.1"
|
||||||
|
click-repl = ">=0.2.0"
|
||||||
|
kombu = ">=5.2.3,<6.0"
|
||||||
|
pytz = ">=2021.3"
|
||||||
|
vine = ">=5.0.0,<6.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
arangodb = ["pyArango (>=1.3.2)"]
|
||||||
|
auth = ["cryptography"]
|
||||||
|
azureblockblob = ["azure-storage-blob (==12.9.0)"]
|
||||||
|
brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"]
|
||||||
|
cassandra = ["cassandra-driver (<3.21.0)"]
|
||||||
|
consul = ["python-consul2"]
|
||||||
|
cosmosdbsql = ["pydocumentdb (==2.3.2)"]
|
||||||
|
couchbase = ["couchbase (>=3.0.0)"]
|
||||||
|
couchdb = ["pycouchdb"]
|
||||||
|
django = ["Django (>=1.11)"]
|
||||||
|
dynamodb = ["boto3 (>=1.9.178)"]
|
||||||
|
elasticsearch = ["elasticsearch"]
|
||||||
|
eventlet = ["eventlet (>=0.32.0)"]
|
||||||
|
gevent = ["gevent (>=1.5.0)"]
|
||||||
|
librabbitmq = ["librabbitmq (>=1.5.0)"]
|
||||||
|
memcache = ["pylibmc"]
|
||||||
|
mongodb = ["pymongo[srv] (>=3.11.1)"]
|
||||||
|
msgpack = ["msgpack"]
|
||||||
|
pymemcache = ["python-memcached"]
|
||||||
|
pyro = ["pyro4"]
|
||||||
|
pytest = ["pytest-celery"]
|
||||||
|
redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"]
|
||||||
|
s3 = ["boto3 (>=1.9.125)"]
|
||||||
|
slmq = ["softlayer-messaging (>=1.0.3)"]
|
||||||
|
solar = ["ephem"]
|
||||||
|
sqlalchemy = ["sqlalchemy"]
|
||||||
|
sqs = ["kombu[sqs]"]
|
||||||
|
tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"]
|
||||||
|
yaml = ["PyYAML (>=3.10)"]
|
||||||
|
zookeeper = ["kazoo (>=1.3.1)"]
|
||||||
|
zstd = ["zstandard"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2022.12.7"
|
version = "2022.12.7"
|
||||||
@ -437,6 +519,56 @@ files = [
|
|||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click-didyoumean"
|
||||||
|
version = "0.3.0"
|
||||||
|
description = "Enables git-like *did-you-mean* feature in click"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6.2,<4.0.0"
|
||||||
|
files = [
|
||||||
|
{file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"},
|
||||||
|
{file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
click = ">=7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click-plugins"
|
||||||
|
version = "1.1.1"
|
||||||
|
description = "An extension module for click to enable registering CLI commands via setuptools entry-points."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"},
|
||||||
|
{file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
click = ">=4.0"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click-repl"
|
||||||
|
version = "0.2.0"
|
||||||
|
description = "REPL plugin for Click"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"},
|
||||||
|
{file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
click = "*"
|
||||||
|
prompt-toolkit = "*"
|
||||||
|
six = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmake"
|
name = "cmake"
|
||||||
version = "3.26.1"
|
version = "3.26.1"
|
||||||
@ -844,6 +976,38 @@ files = [
|
|||||||
{file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"},
|
{file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kombu"
|
||||||
|
version = "5.2.4"
|
||||||
|
description = "Messaging library for Python."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "kombu-5.2.4-py3-none-any.whl", hash = "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"},
|
||||||
|
{file = "kombu-5.2.4.tar.gz", hash = "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
amqp = ">=5.0.9,<6.0.0"
|
||||||
|
vine = "*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
azureservicebus = ["azure-servicebus (>=7.0.0)"]
|
||||||
|
azurestoragequeues = ["azure-storage-queue"]
|
||||||
|
consul = ["python-consul (>=0.6.0)"]
|
||||||
|
librabbitmq = ["librabbitmq (>=2.0.0)"]
|
||||||
|
mongodb = ["pymongo (>=3.3.0,<3.12.1)"]
|
||||||
|
msgpack = ["msgpack"]
|
||||||
|
pyro = ["pyro4"]
|
||||||
|
qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"]
|
||||||
|
redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"]
|
||||||
|
slmq = ["softlayer-messaging (>=1.0.3)"]
|
||||||
|
sqlalchemy = ["sqlalchemy"]
|
||||||
|
sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"]
|
||||||
|
yaml = ["PyYAML (>=3.10)"]
|
||||||
|
zookeeper = ["kazoo (>=1.3.1)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain"
|
name = "langchain"
|
||||||
version = "0.0.115"
|
version = "0.0.115"
|
||||||
@ -1528,6 +1692,21 @@ files = [
|
|||||||
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"]
|
docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"]
|
||||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
|
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prompt-toolkit"
|
||||||
|
version = "3.0.38"
|
||||||
|
description = "Library for building powerful interactive command lines in Python"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7.0"
|
||||||
|
files = [
|
||||||
|
{file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"},
|
||||||
|
{file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
wcwidth = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "psutil"
|
name = "psutil"
|
||||||
version = "5.9.4"
|
version = "5.9.4"
|
||||||
@ -1808,6 +1987,25 @@ files = [
|
|||||||
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redis"
|
||||||
|
version = "4.5.4"
|
||||||
|
description = "Python client for Redis database and key-value store"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "redis-4.5.4-py3-none-any.whl", hash = "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2"},
|
||||||
|
{file = "redis-4.5.4.tar.gz", hash = "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
async-timeout = {version = ">=4.0.2", markers = "python_version <= \"3.11.2\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
hiredis = ["hiredis (>=1.0.0)"]
|
||||||
|
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "2023.3.23"
|
version = "2023.3.23"
|
||||||
@ -2411,6 +2609,15 @@ category = "dev"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
files = [
|
files = [
|
||||||
|
{file = "triton-2.0.0-1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38806ee9663f4b0f7cd64790e96c579374089e58f49aac4a6608121aa55e2505"},
|
||||||
|
{file = "triton-2.0.0-1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:226941c7b8595219ddef59a1fdb821e8c744289a132415ddd584facedeb475b1"},
|
||||||
|
{file = "triton-2.0.0-1-cp36-cp36m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4c9fc8c89874bc48eb7e7b2107a9b8d2c0bf139778637be5bfccb09191685cfd"},
|
||||||
|
{file = "triton-2.0.0-1-cp37-cp37m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d2684b6a60b9f174f447f36f933e9a45f31db96cb723723ecd2dcfd1c57b778b"},
|
||||||
|
{file = "triton-2.0.0-1-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9d4978298b74fcf59a75fe71e535c092b023088933b2f1df933ec32615e4beef"},
|
||||||
|
{file = "triton-2.0.0-1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:74f118c12b437fb2ca25e1a04759173b517582fcf4c7be11913316c764213656"},
|
||||||
|
{file = "triton-2.0.0-1-pp37-pypy37_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9618815a8da1d9157514f08f855d9e9ff92e329cd81c0305003eb9ec25cc5add"},
|
||||||
|
{file = "triton-2.0.0-1-pp38-pypy38_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aca3303629cd3136375b82cb9921727f804e47ebee27b2677fef23005c3851a"},
|
||||||
|
{file = "triton-2.0.0-1-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e3e13aa8b527c9b642e3a9defcc0fbd8ffbe1c80d8ac8c15a01692478dc64d8a"},
|
||||||
{file = "triton-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f05a7e64e4ca0565535e3d5d3405d7e49f9d308505bb7773d21fb26a4c008c2"},
|
{file = "triton-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f05a7e64e4ca0565535e3d5d3405d7e49f9d308505bb7773d21fb26a4c008c2"},
|
||||||
{file = "triton-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4b99ca3c6844066e516658541d876c28a5f6e3a852286bbc97ad57134827fd"},
|
{file = "triton-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb4b99ca3c6844066e516658541d876c28a5f6e3a852286bbc97ad57134827fd"},
|
||||||
{file = "triton-2.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47b4d70dc92fb40af553b4460492c31dc7d3a114a979ffb7a5cdedb7eb546c08"},
|
{file = "triton-2.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47b4d70dc92fb40af553b4460492c31dc7d3a114a979ffb7a5cdedb7eb546c08"},
|
||||||
@ -2496,6 +2703,30 @@ h11 = ">=0.8"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vine"
|
||||||
|
version = "5.0.0"
|
||||||
|
description = "Promises, promises, promises."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.6"
|
||||||
|
files = [
|
||||||
|
{file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"},
|
||||||
|
{file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wcwidth"
|
||||||
|
version = "0.2.6"
|
||||||
|
description = "Measures the displayed width of unicode strings in a terminal"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
|
||||||
|
{file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wheel"
|
name = "wheel"
|
||||||
version = "0.40.0"
|
version = "0.40.0"
|
||||||
@ -2633,4 +2864,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "e6e707098fa68cc228ba4bd454533f460f354991091144c0e3bde29ae0b409e1"
|
content-hash = "3a7cbc62858401d620de4eeab75906ea8caa97d780632e58e1d6316681228d13"
|
||||||
|
@ -24,6 +24,8 @@ uvicorn = "^0.21.1"
|
|||||||
python-ptrace = "^0.9.8"
|
python-ptrace = "^0.9.8"
|
||||||
jinja2 = "^3.1.2"
|
jinja2 = "^3.1.2"
|
||||||
python-multipart = "^0.0.6"
|
python-multipart = "^0.0.6"
|
||||||
|
celery = "^5.2.7"
|
||||||
|
redis = "^4.5.4"
|
||||||
|
|
||||||
[tool.poetry.group.gpu]
|
[tool.poetry.group.gpu]
|
||||||
optional = true
|
optional = true
|
||||||
|
@ -1,6 +1,25 @@
|
|||||||
const setAnswer = (answer, files) => {
|
const $ = (selector) => document.querySelector(selector);
|
||||||
document.getElementById("answer").textContent = answer;
|
|
||||||
const filesDiv = document.getElementById("response-files");
|
const setLoader = (isLoading) => {
|
||||||
|
const button = $("#submit");
|
||||||
|
const loader = $("#submit-loader");
|
||||||
|
if (isLoading) {
|
||||||
|
button.style.display = "none";
|
||||||
|
loader.style.display = "block";
|
||||||
|
} else {
|
||||||
|
button.style.display = "block";
|
||||||
|
loader.style.display = "none";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const setAnswer = (answer, files = []) => {
|
||||||
|
if (answer) {
|
||||||
|
$("#answer").textContent = answer;
|
||||||
|
} else {
|
||||||
|
$("#answer").innerHTML = createSpinner();
|
||||||
|
}
|
||||||
|
|
||||||
|
const filesDiv = $("#response-files");
|
||||||
filesDiv.innerHTML = "";
|
filesDiv.innerHTML = "";
|
||||||
files.forEach((file) => {
|
files.forEach((file) => {
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
@ -12,10 +31,27 @@ const setAnswer = (answer, files) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const submit = async () => {
|
class EvalApi {
|
||||||
setAnswer("Loading...", []);
|
constructor({ onComplete, onError, onSettle, onLLMEnd, onToolEnd }) {
|
||||||
|
this.executionId = null;
|
||||||
|
this.pollInterval = null;
|
||||||
|
this.onComplete = (answer, files, info) => {
|
||||||
|
onComplete(answer, files, info);
|
||||||
|
onSettle();
|
||||||
|
};
|
||||||
|
this.onError = (error) => {
|
||||||
|
onError(error);
|
||||||
|
onSettle();
|
||||||
|
};
|
||||||
|
this.onLLMEnd = (info) => {
|
||||||
|
onLLMEnd(info);
|
||||||
|
};
|
||||||
|
this.onToolEnd = (info) => {
|
||||||
|
onToolEnd(info);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async uploadFiles(rawfiles) {
|
||||||
const files = [];
|
const files = [];
|
||||||
const rawfiles = document.getElementById("files").files;
|
|
||||||
|
|
||||||
if (rawfiles.length > 0) {
|
if (rawfiles.length > 0) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@ -30,11 +66,12 @@ const submit = async () => {
|
|||||||
files.push(...urls);
|
files.push(...urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
const prompt = document.getElementById("prompt").value;
|
return files;
|
||||||
const session = document.getElementById("session").value;
|
}
|
||||||
|
|
||||||
|
async execute(prompt, session, files) {
|
||||||
try {
|
try {
|
||||||
const response = await fetch("/api/execute", {
|
const response = await fetch("/api/execute/async", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@ -48,14 +85,193 @@ const submit = async () => {
|
|||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
throw new Error(await response.text());
|
throw new Error(await response.text());
|
||||||
}
|
}
|
||||||
const { answer, files: responseFiles } = await response.json();
|
const { id: executionId } = await response.json();
|
||||||
setAnswer(answer, responseFiles);
|
this.executionId = executionId;
|
||||||
|
this.pollInterval = setInterval(this.poll.bind(this), 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setAnswer("Error: " + e.message, []);
|
clearInterval(this.pollInterval);
|
||||||
|
this.onError(e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async poll() {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/execute/async/${this.executionId}`, {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw new Error(await response.text());
|
||||||
|
}
|
||||||
|
const { status, result, info } = await response.json();
|
||||||
|
switch (status) {
|
||||||
|
case "PENDING":
|
||||||
|
break;
|
||||||
|
case "FAILURE":
|
||||||
|
throw new Error("Execution failed");
|
||||||
|
case "LLM_END":
|
||||||
|
this.onLLMEnd(info);
|
||||||
|
break;
|
||||||
|
case "TOOL_END":
|
||||||
|
this.onToolEnd(info);
|
||||||
|
break;
|
||||||
|
case "SUCCESS":
|
||||||
|
clearInterval(this.pollInterval);
|
||||||
|
this.onComplete(result.answer, result.files, info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
clearInterval(this.pollInterval);
|
||||||
|
this.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
setAnswer("");
|
||||||
|
setLoader(true);
|
||||||
|
|
||||||
|
const actions = $("#actions");
|
||||||
|
actions.innerHTML = "";
|
||||||
|
|
||||||
|
let currentActionIndex = 0;
|
||||||
|
|
||||||
|
const onInfo = (info) => {
|
||||||
|
if (currentActionIndex >= info.index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentActionIndex = info.index;
|
||||||
|
const w = document.createElement("div");
|
||||||
|
w.innerHTML = createActionCard(
|
||||||
|
info.index,
|
||||||
|
info.action,
|
||||||
|
info.action_input,
|
||||||
|
info.what_i_did,
|
||||||
|
info.plan,
|
||||||
|
info.observation
|
||||||
|
);
|
||||||
|
actions.appendChild(w);
|
||||||
|
};
|
||||||
|
|
||||||
|
const api = new EvalApi({
|
||||||
|
onSettle: () => setLoader(false),
|
||||||
|
onError: (error) => setAnswer(`Error: ${error.message}`, []),
|
||||||
|
onComplete: (answer, files, info) => {
|
||||||
|
setAnswer(answer, files);
|
||||||
|
onInfo(info);
|
||||||
|
},
|
||||||
|
onLLMEnd: onInfo,
|
||||||
|
onToolEnd: onInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
const prompt = $("#prompt").value;
|
||||||
|
const session = $("#session").value;
|
||||||
|
const files = await api.uploadFiles($("#files").files);
|
||||||
|
|
||||||
|
await api.execute(prompt, session, files);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setRandomSessionId = () => {
|
const setRandomSessionId = () => {
|
||||||
const sessionId = Math.random().toString(36).substring(2, 15);
|
const sessionId = Math.random().toString(36).substring(2, 15);
|
||||||
document.getElementById("session").value = sessionId;
|
$("#session").value = sessionId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createSpinner = () => `
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="spinner-border m-3"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const createActionCard = (
|
||||||
|
index,
|
||||||
|
action,
|
||||||
|
input,
|
||||||
|
whatIdid,
|
||||||
|
plan,
|
||||||
|
observation
|
||||||
|
) => `
|
||||||
|
<div class="accordion m-2">
|
||||||
|
<div class="accordion-item">
|
||||||
|
<h2 class="accordion-header">
|
||||||
|
<button class="accordion-button">
|
||||||
|
<span class="text-secondary">
|
||||||
|
Action #${index}
|
||||||
|
</span>
|
||||||
|
<span class="mx-1">-</span>
|
||||||
|
<span class="fw-bold">
|
||||||
|
${action}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
<div class="accordion-collapse collapse show">
|
||||||
|
<div class="accordion-body">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
${
|
||||||
|
action !== "Final Answer"
|
||||||
|
? `<tr>
|
||||||
|
<th style="width: 100px">Input</th>
|
||||||
|
<td><div>${input}</div></td>
|
||||||
|
</tr>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
<tr>
|
||||||
|
<th style="width: 100px">What I Did</th>
|
||||||
|
<td><div>${whatIdid}</div></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Plan</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
${plan
|
||||||
|
.split("- ")
|
||||||
|
.map((p) => p.trim())
|
||||||
|
.filter((p) => p.length > 0)
|
||||||
|
.map(
|
||||||
|
(p) => `
|
||||||
|
<tr>
|
||||||
|
${
|
||||||
|
p.startsWith("[ ]")
|
||||||
|
? `<td><input class="form-check-input" type="checkbox" /></td>
|
||||||
|
<td>${p.replace("[ ]", "")}</td>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
${
|
||||||
|
p.startsWith("[x]")
|
||||||
|
? `<td><input class="form-check-input" type="checkbox" checked/></td>
|
||||||
|
<td>${p.replace("[x]", "")}</td>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</tr>`
|
||||||
|
)
|
||||||
|
.join("")}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
${
|
||||||
|
action !== "Final Answer"
|
||||||
|
? `<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Observation</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div>${observation}</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
.logo {
|
.logo {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 64px;
|
height: 48px;
|
||||||
width: 64px;
|
width: 48px;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user