Skip to content

Commit

Permalink
Brkrabac/prospector-assistant - adds initial wip support for skills, …
Browse files Browse the repository at this point in the history
…content reduction in debug log, and updated api for wip artifacts (#84)
  • Loading branch information
bkrabach authored Oct 4, 2024
1 parent 2141f55 commit 9edc6cf
Show file tree
Hide file tree
Showing 12 changed files with 825 additions and 38 deletions.
5 changes: 1 addition & 4 deletions assistants/prospector-assistant/assistant/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from .chat import app
from .config import AssistantConfigModel

__all__ = [
"AssistantConfigModel",
"app",
]
__all__ = ["app", "AssistantConfigModel"]
16 changes: 0 additions & 16 deletions assistants/prospector-assistant/assistant/agents/__init__.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from semantic_workbench_assistant.storage import read_model, write_model

if TYPE_CHECKING:
from assistant import AssistantConfigModel
from ..config import AssistantConfigModel


#
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ class Attachment(BaseModel):
#


attachment_tag = "ATTACHMENT"
filename_tag = "FILENAME"
content_tag = "CONTENT"
image_tag = "IMAGE"


class AttachmentAgent:
@staticmethod
async def create_or_update_attachment_from_file(
Expand Down Expand Up @@ -137,7 +143,7 @@ def generate_attachment_messages(
"content": [
{
"type": "text",
"text": f"<ATTACHMENT>\n\t<FILENAME>{attachment.filename}</FILENAME>\n\t<IMAGE>",
"text": f"<{attachment_tag}><{filename_tag}>{attachment.filename}</{filename_tag}><{image_tag}>",
},
{
"type": "image_url",
Expand All @@ -147,24 +153,77 @@ def generate_attachment_messages(
},
{
"type": "text",
"text": "</IMAGE>\n</ATTACHMENT>",
"text": f"</{image_tag}></{attachment_tag}>",
},
],
})
continue

# otherwise, include the content as text within the message content
content_details = (
f"\n\t<FILENAME>{attachment.filename}</FILENAME>\n\t<CONTENT>{attachment.content}</CONTENT>"
)
content_details = f"<{filename_tag}>{attachment.filename}</{filename_tag}><{content_tag}>{attachment.content}</{content_tag}>"

messages.append({
"role": "system",
"content": f"<ATTACHMENT>{content_details}\n</ATTACHMENT>",
"content": f"<{attachment_tag}>{content_details}</{attachment_tag}>",
})

return messages

@staticmethod
def reduce_attachment_payload_from_content(value: Any) -> Any:
"""
Reduce the content of any attachment in the payload to a smaller size.
This method is intended to be used with the debug metadata in the parent assistant that
uses this agent to reduce the size of the content of the attachments in the payload.
The content will be reduced to the first and last `head_tail_length` characters, with a
placeholder in the middle.
"""

# define the length of the head and tail of the content to keep and the placeholder to use in the middle
head_tail_length = 40
placeholder = "<REDUCED_FOR_DEBUG_OUTPUT/>"

# inspect the content and look for the attachment tags
# there are two types of content that we need to handle: text and image
# if the content is an image, we will be reducing the image_url.url value
# if the content is text, we will be reducing the text value if it contains the attachment tag

# start by checking if this is a string or a list of dictionaries
if isinstance(value, str):
# if this is a string, we can assume that this is a text content
# we will be reducing the text value if it contains the attachment tag
if attachment_tag in value:
# just reduce within the content_tag, but still show the head/tail in there
start_index = value.find(f"<{content_tag}>") + len(f"<{content_tag}>")
end_index = value.find(f"</{content_tag}>")
if start_index != -1 and end_index != -1:
return (
value[: start_index + head_tail_length]
+ f"...{placeholder}..."
+ value[end_index - head_tail_length :]
)
elif isinstance(value, list):
# if this is a list, check to see if it contains dictionaries
# and if they contain the attachment tag
# if so, look for and reduce the image_url.url value
is_attachment = False
for item in value:
if isinstance(item, dict):
if "text" in item and attachment_tag in item["text"]:
is_attachment = True
break
if is_attachment:
# reduce the image_url.url value
for item in value:
if isinstance(item, dict) and "image_url" in item:
item["image_url"]["url"] = item["image_url"]["url"][:head_tail_length] + f"...{placeholder}"
return value

# if the content is not an attachment, return the original value
return value


# endregion

Expand Down
87 changes: 87 additions & 0 deletions assistants/prospector-assistant/assistant/agents/skills/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import pathlib
from typing import Annotated

from pydantic import BaseModel, Field
from semantic_workbench_assistant.config import UISchema

# The semantic workbench app uses react-jsonschema-form for rendering
# dynamic configuration forms based on the configuration model and UI schema
# See: https://rjsf-team.github.io/react-jsonschema-form/docs/
# Playground / examples: https://rjsf-team.github.io/react-jsonschema-form/

# The UI schema can be used to customize the appearance of the form. Use
# the UISchema class to define the UI schema for specific fields in the
# configuration model.


#
# region Helpers
#


# helper for loading an include from a text file
def load_text_include(filename) -> str:
# get directory relative to this module
directory = pathlib.Path(__file__).parent.parent.parent

# get the file path for the prompt file
file_path = directory / "text_includes" / filename

# read the prompt from the file
return file_path.read_text()


# endregion


#
# region Agent Configuration
#


class ChatDriverConfig(BaseModel):
instructions: Annotated[
str,
Field(
title="Instructions",
description="The prompt used to instruct the behavior of the AI assistant.",
),
UISchema(widget="textarea"),
] = "You are a helpful assistant."

openai_model: Annotated[
str,
Field(title="OpenAI Model", description="The OpenAI model to use for chat driver."),
] = "gpt-4o"


class SkillsAgentConfigModel(BaseModel):
enable_skills: Annotated[
bool,
Field(
description=load_text_include("skills_agent_enable_skills.md"),
),
UISchema(enable_markdown_in_description=True),
] = False

welcome_message: Annotated[
str,
Field(
title="Welcome Message",
description="The message to display when the conversation starts.",
),
UISchema(widget="textarea"),
] = "Hello! How can I help you today?"

chat_driver_config: Annotated[
ChatDriverConfig,
Field(
title="Chat Driver Configuration",
description="The configuration for the chat driver.",
),
] = ChatDriverConfig()

# add any additional configuration fields


# endregion
Loading

0 comments on commit 9edc6cf

Please sign in to comment.