Skip to content

Add docs list agent #882

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 54 additions & 7 deletions lumen/ai/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class SourceAgent(Agent):

requires = param.List(default=[], readonly=True)

provides = param.List(default=["source"], readonly=True)
provides = param.List(default=["source", "document_sources"], readonly=True)

on_init = param.Boolean(default=True)

Expand Down Expand Up @@ -293,12 +293,6 @@ class TableListAgent(Agent):
Not useful for gathering information about the tables.
""")

prompts = param.Dict(
default={
"main": {"template": PROMPTS_DIR / "TableListAgent" / "main.jinja2"},
}
)

requires = param.List(default=["source"], readonly=True)

_extensions = ('tabulator',)
Expand Down Expand Up @@ -343,6 +337,59 @@ async def respond(
return table_list


class DocumentListAgent(Agent):
"""
The DocumentListAgent lists all available documents provided by the user.
"""

purpose = param.String(default="""
Renders a list of all availables documents to the user and lets the user
pick one. Not useful for gathering details about the documents;
use ChatAgent for that instead.
""")

requires = param.List(default=["document_sources"], readonly=True)

_extensions = ('tabulator',)

@classmethod
async def applies(cls, memory: _Memory) -> bool:
sources = memory.get("document_sources")
if not sources:
return False # source not loaded yet; always apply
return len(sources) > 1

def _use_document(self, event):
if event.column != "show":
return
document = self._df.iloc[event.row, 0]
self.interface.send(f"Tell me about: {document!r}")

async def respond(
self,
messages: list[Message],
render_output: bool = False,
step_title: str | None = None,
) -> Any:
# extract the filename, following this pattern `Filename: 'filename'``
sources = [doc["metadata"].get("filename", "untitled") for doc in self._memory["document_sources"]]
self._df = pd.DataFrame({"Documents": sources})
document_list = pn.widgets.Tabulator(
self._df,
buttons={'show': '<i class="fa fa-eye"></i>'},
show_index=False,
min_height=150,
min_width=350,
widths={'Table': '90%'},
disabled=True,
page_size=10,
header_filters=True
)
document_list.on_click(self._use_document)
self.interface.stream(document_list, user="Lumen")
return document_list


class LumenBaseAgent(Agent):

user = param.String(default="Lumen")
Expand Down
5 changes: 2 additions & 3 deletions lumen/ai/controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def _add_document(
return 0

metadata = {
"filename": document_controls.filename,
"filename": f"{document_controls.filename}.{document_controls.extension}",
"comments": document_controls._metadata_input.value,
}
document = {"text": text, "metadata": metadata}
Expand All @@ -312,8 +312,7 @@ def _add_document(
else:
self._memory["document_sources"].append(document)
else:
with param.discard_events(self._memory):
self._memory["document_sources"] = [document]
self._memory["document_sources"] = [document]
return 1

@param.depends("add", watch=True)
Expand Down
2 changes: 1 addition & 1 deletion lumen/ai/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ async def _execute_graph_node(self, node: ExecutionNode, messages: list[Message]
log_debug(f"\033[96m{agent_name} successfully completed\033[0m", show_sep=False, show_length=False)

unprovided = [p for p in subagent.provides if p not in self._memory]
if unprovided:
if unprovided and not any(unprovided):
step.failed_title = f"{agent_name} did not provide {', '.join(unprovided)}. Aborting the plan."
raise RuntimeError(f"{agent_name} failed to provide declared context.")
step.stream(f"\n\n`{agent_name}` agent successfully completed the following task:\n\n> {instruction}", replace=True)
Expand Down
5 changes: 0 additions & 5 deletions lumen/ai/prompts/TableListAgent/main.jinja2

This file was deleted.

6 changes: 3 additions & 3 deletions lumen/ai/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@
from ..transforms.sql import SQLLimit
from ..util import log
from .agents import (
AnalysisAgent, AnalystAgent, ChatAgent, SourceAgent, SQLAgent,
TableListAgent, VegaLiteAgent,
AnalysisAgent, AnalystAgent, ChatAgent, DocumentListAgent, SourceAgent,
SQLAgent, TableListAgent, VegaLiteAgent,
)
from .components import SplitJS
from .controls import SourceControls
Expand Down Expand Up @@ -87,7 +87,7 @@ class UI(Viewer):
)

default_agents = param.List(default=[
TableListAgent, ChatAgent, AnalystAgent, SourceAgent, SQLAgent, VegaLiteAgent
TableListAgent, ChatAgent, DocumentListAgent, AnalystAgent, SourceAgent, SQLAgent, VegaLiteAgent
], doc="""List of default agents which will always be added.""")

export_functions = param.Dict(default={}, doc="""
Expand Down
Loading