Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/libraries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ jobs:
run: uv python install ${{ matrix.python-version }}

- name: Install and run tests
run: make test
run: make -j $(nproc) test
18 changes: 15 additions & 3 deletions assistants/document-assistant/assistant/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

import deepmerge
from assistant_extensions import dashboard_card, navigator
from assistant_extensions.chat_context_toolkit.archive import ArchiveTaskQueues
from assistant_extensions.attachments import get_attachments
from assistant_extensions.chat_context_toolkit.archive import ArchiveTaskQueues, construct_archive_summarizer
from assistant_extensions.chat_context_toolkit.message_history import construct_attachment_summarizer
from assistant_extensions.mcp import MCPServerConfig
from chat_context_toolkit.archive import ArchiveTaskConfig
from content_safety.evaluators import CombinedContentSafetyEvaluator
Expand Down Expand Up @@ -209,11 +211,21 @@ async def on_message_created(
)
)

attachments = await get_attachments(
context=context,
summarizer=construct_attachment_summarizer(
service_config=config.generative_ai_fast_client_config.service_config,
request_config=config.generative_ai_fast_client_config.request_config,
),
)
await archive_task_queues.enqueue_run(
context=context,
service_config=config.generative_ai_client_config.service_config,
request_config=config.generative_ai_client_config.request_config,
attachments=attachments,
archive_task_config=ArchiveTaskConfig(chunk_token_count_threshold=30_000),
archive_summarizer=construct_archive_summarizer(
service_config=config.generative_ai_fast_client_config.service_config,
request_config=config.generative_ai_fast_client_config.request_config,
),
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Callable, Iterable

from assistant_drive import Drive
from chat_context_toolkit.virtual_filesystem import DirectoryEntry, FileEntry, MountPoint, WriteToolDefinition
from chat_context_toolkit.virtual_filesystem import DirectoryEntry, FileEntry, MountPoint
from semantic_workbench_assistant.assistant_app import ConversationContext

from assistant.filesystem._filesystem import AttachmentsExtension, _get_attachments, log_and_send_message_on_error
Expand All @@ -17,11 +17,6 @@ def __init__(self, context: ConversationContext, attachments_extension: Attachme
self.context = context
self.attachments_extension = attachments_extension

@property
def write_tools(self) -> Iterable[WriteToolDefinition]:
"""Get the list of write tools provided by this file system provider."""
return []

async def list_directory(self, path: str) -> Iterable[DirectoryEntry | FileEntry]:
"""
List files and directories at the specified path.
Expand Down Expand Up @@ -86,11 +81,6 @@ def __init__(self, context: ConversationContext, drive_provider: Callable[[Conve
self.context = context
self.drive_provider = drive_provider

@property
def write_tools(self) -> Iterable[WriteToolDefinition]:
"""Get the list of write tools provided by this file system provider."""
return []

async def list_directory(self, path: str) -> Iterable[DirectoryEntry | FileEntry]:
"""
List files and directories at the specified path.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,13 @@


async def handle_completion(
sampling_handler: OpenAISamplingHandler,
step_result: StepResult,
completion: ParsedChatCompletion | ChatCompletion,
mcp_sessions: List[MCPSession],
context: ConversationContext,
request_config: OpenAIRequestConfig,
silence_token: str,
metadata_key: str,
response_start_time: float,
attachments_extension: AttachmentsExtension,
guidance_enabled: bool,
virtual_filesystem: VirtualFileSystem,
) -> StepResult:
Expand Down
3 changes: 0 additions & 3 deletions assistants/document-assistant/assistant/response/responder.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,16 +324,13 @@ async def _step(self, step_count) -> StepResult:
await update_dynamic_ui_state(self.context, tool_call.arguments)

step_result = await handle_completion(
self.sampling_handler,
step_result,
completion,
self.mcp_sessions,
self.context,
self.config.generative_ai_client_config.request_config,
"SILENCE", # TODO: This is not being used correctly.
f"respond_to_conversation:step_{step_count}",
response_start_time,
self.attachments_extension,
self.config.orchestration.guidance.enabled,
self.virtual_filesystem,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ._ls_tool import LsTool
from ._ls_tool import LsTool, LsToolOptions
from ._tools import ToolCollection, tool_result_to_string
from ._view_tool import ViewTool

__all__ = ["LsTool", "ToolCollection", "ViewTool", "tool_result_to_string"]
__all__ = ["LsTool", "ToolCollection", "ViewTool", "tool_result_to_string", "LsToolOptions"]
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
from dataclasses import dataclass
from typing import Iterable

from openai.types.chat import ChatCompletionContentPartTextParam, ChatCompletionToolParam

from chat_context_toolkit.virtual_filesystem._types import DirectoryEntry, FileEntry, ToolDefinition
from chat_context_toolkit.virtual_filesystem._virtual_filesystem import VirtualFileSystem


@dataclass
class LsToolOptions:
tool_name: str = "ls"
"""Name of the tool provided to the LLM."""
tool_description: str = "List files and directories at the specified path. Root directories: {root_path_list}"
"""Description of the tool provided to the LLM."""
path_argument_description: str = "The path to list (e.g., '/', '/docs', '/docs/subdir)"
"""Description of the 'path' argument."""


class LsTool(ToolDefinition):
"""Tool for listing files and directories in the virtual file system."""

def __init__(self, virtual_filesystem: VirtualFileSystem):
def __init__(self, virtual_filesystem: VirtualFileSystem, options: LsToolOptions = LsToolOptions()) -> None:
self.virtual_filesystem = virtual_filesystem
self.options = options

@property
def tool_param(self) -> ChatCompletionToolParam:
Expand All @@ -20,14 +33,14 @@ def tool_param(self) -> ChatCompletionToolParam:
return ChatCompletionToolParam(
type="function",
function={
"name": "ls",
"description": "List files and directories at the specified path. Root directories: " + mount_list,
"name": self.options.tool_name,
"description": self.options.tool_description.format(root_path_list=mount_list),
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The path to list (e.g., '/', '/docs', '/docs/subdir')",
"description": self.options.path_argument_description.format(root_path_list=mount_list),
}
},
"required": ["path"],
Expand All @@ -39,7 +52,7 @@ async def execute(self, args: dict) -> str | Iterable[ChatCompletionContentPartT
"""Execute the built-in ls tool to list directory contents."""
path = args.get("path")
if not path:
return "Error: 'path' argument is required for the ls tool"
return f"Error: 'path' argument is required for the {self.options.tool_name} tool"

try:
entries = await self.virtual_filesystem.list_directory(path)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,42 @@
from dataclasses import dataclass
from typing import Iterable

from openai.types.chat import ChatCompletionContentPartTextParam, ChatCompletionToolParam

from chat_context_toolkit.virtual_filesystem._types import ToolDefinition
from chat_context_toolkit.virtual_filesystem._virtual_filesystem import VirtualFileSystem
from openai.types.chat import ChatCompletionContentPartTextParam, ChatCompletionToolParam


@dataclass
class ViewToolOptions:
tool_name: str = "view"
"""Name of the tool provided to the LLM."""
tool_description: str = "Read the contents of a file at the specified path"
"""Description of the tool provided to the LLM."""
path_argument_description: str = "The path to the file to read (e.g., '/docs/file.txt')"
"""Description of the 'path' argument."""


class ViewTool(ToolDefinition):
"""Tool for viewing the contents of a file in the virtual file system."""

def __init__(self, virtual_filesystem: VirtualFileSystem, tool_name: str = "view"):
def __init__(self, virtual_filesystem: VirtualFileSystem, options: ViewToolOptions = ViewToolOptions()) -> None:
self.virtual_filesystem = virtual_filesystem
self.tool_name = tool_name
self.options = options

@property
def tool_param(self) -> ChatCompletionToolParam:
return ChatCompletionToolParam(
type="function",
function={
"name": self.tool_name,
"description": "Read the contents of a file at the specified path",
"name": self.options.tool_name,
"description": self.options.tool_description,
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The path to the file to read (e.g., '/docs/file.txt')",
"description": self.options.path_argument_description,
}
},
"required": ["path"],
Expand All @@ -35,12 +48,12 @@ async def execute(self, args: dict) -> str | Iterable[ChatCompletionContentPartT
"""Execute the built-in view tool to read file contents."""
path = args.get("path")
if not path:
return "Error: 'path' argument is required for the view tool"
return f"Error: 'path' argument is required for the {self.options.tool_name} tool"

try:
file_content = await self.virtual_filesystem.read_file(path)
except FileNotFoundError:
return f"Error: File at path {path} not found. Please pay attention to the available files and try again."

result = f"<file path={path}>\n{file_content}\n</file>"
result = f'<file path="{path}">\n{file_content}\n</file>'
return result
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ async def test_builtin_view_tool_reads_file():
result = await view_tool.execute({"path": "/docs/file1.txt"})

mock_vfs.read_file.assert_called_once_with("/docs/file1.txt")
assert result == "<file path=/docs/file1.txt>\nHello World\n</file>"
assert result == '<file path="/docs/file1.txt">\nHello World\n</file>'


async def test_view_tool_error_handling():
Expand All @@ -25,4 +25,7 @@ async def test_view_tool_error_handling():
# view on non-existent file should raise error
view_tool = ViewTool(mock_vfs)
result = await view_tool.execute({"path": "/nonexistent.txt"})
assert result == "Error: File at path /nonexistent.txt not found. Please pay attention to the available files and try again."
assert (
result
== "Error: File at path /nonexistent.txt not found. Please pay attention to the available files and try again."
)