-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adds python example assistant and recent workbench app/service improv…
…ements (#21)
- Loading branch information
Showing
21 changed files
with
857 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[tool.black] | ||
line-length = 120 | ||
target-version = ["py310", "py311"] | ||
preview = true | ||
unstable = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# Description: Example of .env file | ||
# Usage: Copy this file to .env and set the values | ||
|
||
# The ASSISTANT__ prefix is used to group all the environment variables related to the assistant service. | ||
ASSISTANT__ENABLE_DEBUG_OUTPUT=True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[flake8] | ||
max-line-length = 120 | ||
exclude = .git,__pycache__,build,dist,venv,.venv,.env,*.egg,*.egg-info,*.egg-info/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
__pycache__ | ||
.pytest_cache | ||
*.egg* | ||
.data | ||
.venv | ||
venv | ||
.env | ||
|
||
poetry.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
{ | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"type": "node", | ||
"request": "launch", | ||
"name": "semantic-workbench-app", | ||
"cwd": "${workspaceFolder}/../../semantic-workbench/v1/app", | ||
"skipFiles": ["<node_internals>/**"], | ||
"console": "integratedTerminal", | ||
"runtimeExecutable": "npm", | ||
"runtimeArgs": ["run", "dev"] | ||
}, | ||
{ | ||
"type": "debugpy", | ||
"request": "launch", | ||
"name": "semantic-workbench-service", | ||
"cwd": "${workspaceFolder}/../../semantic-workbench/v1/service", | ||
"module": "semantic_workbench_service.start", | ||
"justMyCode": false, | ||
"consoleTitle": "semantic-workbench-service", | ||
"args": ["--host", "0.0.0.0", "--port", "3000"] | ||
}, | ||
{ | ||
"type": "debugpy", | ||
"request": "launch", | ||
"name": "python-example01 (assistant)", | ||
"cwd": "${workspaceFolder}", | ||
"module": "semantic_workbench_assistant.start", | ||
"args": ["assistant.chat:app", "--port", "3003"], | ||
"consoleTitle": "${workspaceFolderBasename}", | ||
"envFile": "${workspaceFolder}/.env" | ||
} | ||
], | ||
"compounds": [ | ||
{ | ||
"name": "semantic-workbench", | ||
"configurations": ["semantic-workbench-app", "semantic-workbench-service"] | ||
}, | ||
{ | ||
"name": "example assistant and semantic-workbench", | ||
"configurations": [ | ||
"semantic-workbench-app", | ||
"semantic-workbench-service", | ||
"python-example01 (assistant)" | ||
] | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
{ | ||
"black-formatter.args": ["--config", "./.black.toml"], | ||
"editor.bracketPairColorization.enabled": true, | ||
"editor.codeActionsOnSave": { | ||
"source.organizeImports": "explicit", | ||
"source.fixAll": "explicit" | ||
}, | ||
"editor.guides.bracketPairs": "active", | ||
"editor.formatOnPaste": true, | ||
"editor.formatOnType": true, | ||
"editor.formatOnSave": true, | ||
"files.eol": "\n", | ||
"files.trimTrailingWhitespace": true, | ||
"flake8.args": ["--config", "./.flake8"], | ||
"isort.args": ["--profile", "black", "--gitignore"], | ||
"[json]": { | ||
"editor.defaultFormatter": "esbenp.prettier-vscode", | ||
"editor.formatOnSave": true | ||
}, | ||
"[jsonc]": { | ||
"editor.defaultFormatter": "esbenp.prettier-vscode", | ||
"editor.formatOnSave": true | ||
}, | ||
"python.analysis.autoFormatStrings": true, | ||
"python.analysis.autoImportCompletions": true, | ||
"python.analysis.diagnosticMode": "workspace", | ||
"python.analysis.exclude": [ | ||
"**/.venv/**", | ||
"**/.data/**", | ||
"**/__pycache__/**" | ||
], | ||
"python.analysis.fixAll": ["source.unusedImports"], | ||
"python.analysis.inlayHints.functionReturnTypes": true, | ||
"python.analysis.typeCheckingMode": "basic", | ||
"python.defaultInterpreterPath": "${workspaceFolder}/.venv", | ||
"[python]": { | ||
"editor.defaultFormatter": "ms-python.black-formatter", | ||
"editor.codeActionsOnSave": { | ||
"source.unusedImports": "explicit", | ||
"source.organizeImports": "explicit", | ||
"source.fixAll": "explicit", | ||
"source.formatDocument": "explicit" | ||
} | ||
}, | ||
"search.exclude": { | ||
"**/.venv": true, | ||
"**/.data": true, | ||
"**/__pycache__": true | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
repo_root = $(shell git rev-parse --show-toplevel) | ||
|
||
.DEFAULT_GOAL := venv | ||
|
||
include $(repo_root)/build-tools/makefiles/poetry.mk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
A python chat assistant example that echos the user's input. | ||
|
||
## Pre-requisites | ||
|
||
- Complete the steps in either: | ||
- [main README](../../README.md) | ||
- [GitHub Codespaces / devcontainers README](../../.devcontainer/README.md) | ||
- Set up and verify that the workbench app and service are running | ||
- Stop the services and open the `python-examples01.code-workspace` in VS Code | ||
|
||
## Steps | ||
|
||
- Use VS Code > Run and Debug > `example assistant and semantic-workbench` to start the assistant. | ||
- If running in a devcontainer, follow the instructions in [GitHub Codespaces / devcontainers README](../../.devcontainer/README.md) for any additional steps. | ||
- Return to the workbench app to interact with the assistant | ||
- Add a new assistant from the main menu of the app, choose `Python Example 01 Assistant` | ||
- Click the newly created assistant to configure and interact with it |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import logging | ||
from typing import AsyncContextManager, Callable, TypeVar | ||
from uuid import UUID | ||
|
||
from semantic_workbench_api_model.workbench_model import ( | ||
ConversationEvent, | ||
ConversationEventType, | ||
NewConversationMessage, | ||
) | ||
from semantic_workbench_assistant import assistant_service | ||
from semantic_workbench_assistant.assistant_base import ( | ||
AssistantBase, | ||
AssistantInstance, | ||
SimpleAssistantConfigStorage, | ||
) | ||
|
||
from assistant.config import AssistantConfigModel | ||
|
||
from . import config | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
# Example built on top of the Assistant base | ||
# This example demonstrates how to extend the Assistant base | ||
# to add additional configuration fields and UI schema for the configuration fields | ||
# and how to create a new Assistant that uses the extended configuration model | ||
|
||
# Comments marked with "Required", "Optional", and "Custom" indicate the type of code that follows | ||
# Required: code that is required to be implemented for any Assistant | ||
# Optional: code that is optional to implement for any Assistant, allowing for customization | ||
# Custom: code that was added specifically for this example | ||
|
||
# Modify the config.py file to add any additional configuration fields | ||
ConfigT = TypeVar("ConfigT", bound=AssistantConfigModel) | ||
|
||
service_id = "python-example01-assistant.python-example" | ||
service_name = "Python Example 01 Assistant" | ||
service_description = "A starter for building a chat assistant using the Semantic Workbench Assistant SDK." | ||
|
||
|
||
class ChatAssistant(AssistantBase): | ||
|
||
# Optional: override the __init__ method to add any additional initialization logic | ||
def __init__( | ||
self, | ||
register_lifespan_handler: Callable[[Callable[[], AsyncContextManager[None]]], None], | ||
service_id=service_id, | ||
service_name=service_name, | ||
service_description=service_description, | ||
) -> None: | ||
|
||
super().__init__( | ||
register_lifespan_handler=register_lifespan_handler, | ||
service_id=service_id, | ||
service_name=service_name, | ||
service_description=service_description, | ||
config_storage=SimpleAssistantConfigStorage[config.AssistantConfigModel]( | ||
cls=config.AssistantConfigModel, | ||
default_config=config.AssistantConfigModel(), | ||
ui_schema=config.ui_schema, | ||
), | ||
) | ||
|
||
# Optional: Override the on_workbench_event method to provide custom handling of conversation events for this | ||
# assistant | ||
async def on_workbench_event( | ||
self, | ||
assistant_instance: AssistantInstance, | ||
event: ConversationEvent, | ||
) -> None: | ||
# add any additional event processing logic here | ||
match event.event: | ||
|
||
case ConversationEventType.message_created | ConversationEventType.conversation_created: | ||
# replace the following with your own logic for processing a message created event | ||
return await self.respond_to_conversation(assistant_instance.id, event.conversation_id) | ||
|
||
case _: | ||
# add any additional event processing logic here | ||
pass | ||
|
||
# Custom: Implement a custom method to respond to a conversation | ||
async def respond_to_conversation(self, assistant_id: str, conversation_id: UUID) -> None: | ||
# get the conversation client | ||
conversation_client = self.workbench_client.for_conversation(assistant_id, str(conversation_id)) | ||
|
||
# get the assistant's messages | ||
messages_response = await conversation_client.get_messages() | ||
if len(messages_response.messages) == 0: | ||
# unexpected, no messages in the conversation | ||
return None | ||
|
||
# get the last message | ||
last_message = messages_response.messages[-1] | ||
|
||
# check if the last message was sent by this assistant | ||
if last_message.sender.participant_id == assistant_id: | ||
# ignore messages from this assistant | ||
return | ||
|
||
# get the assistant's configuration, supports overwriting defaults from environment variables | ||
assistant_config = self._config_storage.get_with_defaults_overwritten_from_env(assistant_id) | ||
|
||
# add your custom logic here for generating a response to the last message | ||
# example: for now, just echo the last message back to the conversation | ||
|
||
# send a new message with the echo response | ||
await conversation_client.send_messages( | ||
NewConversationMessage( | ||
content=f"echo: {last_message.content}", | ||
metadata=( | ||
{ | ||
"debug": { | ||
"source": "echo", | ||
"original_message": last_message, | ||
} | ||
} | ||
if assistant_config.enable_debug_output | ||
else None | ||
), | ||
) | ||
) | ||
|
||
# add any additional methods or overrides here | ||
# see assistant_base.AssistantBase for examples | ||
|
||
|
||
# Required: Create an instance of the Assistant class | ||
app = assistant_service.create_app( | ||
lambda lifespan: ChatAssistant( | ||
register_lifespan_handler=lifespan.register_handler, | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from typing import Annotated, Self | ||
from pydantic import Field | ||
from semantic_workbench_assistant import config, assistant_base | ||
|
||
# The semantic workbench app uses react-jsonschema-form for rendering | ||
# dyanmic 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 workbench app builds dynamic forms based on the configuration model and UI schema | ||
class AssistantConfigModel(assistant_base.AssistantConfigModel): | ||
enable_debug_output: Annotated[ | ||
bool, | ||
Field( | ||
title="Include Debug Output", | ||
description="Include debug output on conversation messages.", | ||
), | ||
] = False | ||
|
||
def overwrite_defaults_from_env(self) -> Self: | ||
""" | ||
Overwrite string fields that currently have their default values. Values are | ||
overwritten with values from environment variables or .env file. If a field | ||
is a BaseModel, it will be recursively updated. | ||
""" | ||
updated = config.overwrite_defaults_from_env(self, prefix="assistant", separator="__") | ||
|
||
# Custom: add any additional handling for nested models | ||
|
||
return updated | ||
|
||
# add any additional configuration fields | ||
|
||
|
||
ui_schema = { | ||
# add UI schema for the additional configuration fields | ||
# see: https://rjsf-team.github.io/react-jsonschema-form/docs/api-reference/uiSchema | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
[tool.poetry] | ||
name = "assistant" | ||
version = "0.1.0" | ||
description = "Example of a python Semantic Workbench assistant." | ||
authors = ["Semantic Workbench Team"] | ||
readme = "README.md" | ||
packages = [{ include = "assistant" }] | ||
|
||
[tool.poetry.dependencies] | ||
python = "~3.11" | ||
openai = "^1.3.9" | ||
|
||
semantic-workbench-api-model = { path = "../../semantic-workbench/v1/service/semantic-workbench-api-model", develop = true, extras=["dev"] } | ||
semantic-workbench-assistant = { path = "../../semantic-workbench/v1/service/semantic-workbench-assistant", develop = true, extras=["dev"] } | ||
semantic-workbench-service = { path = "../../semantic-workbench/v1/service/semantic-workbench-service", develop = true, extras=["dev"] } | ||
|
||
black = { version = "^24.3.0", optional = true } | ||
flake8 = { version = "^6.1.0", optional = true } | ||
|
||
[tool.poetry.extras] | ||
dev = ["black", "flake8"] | ||
|
||
[build-system] | ||
requires = ["poetry-core>=1.2.0"] | ||
build-backend = "poetry.core.masonry.api" | ||
|
||
[tool.isort] | ||
multi_line_output = 3 | ||
include_trailing_comma = true | ||
force_grid_wrap = 0 | ||
line_length = 120 | ||
profile = "black" | ||
|
||
[tool.pyright] | ||
exclude = ["venv", ".venv"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
{ | ||
"folders": [ | ||
{ | ||
"path": ".", | ||
"name": "examples/python-examples01" | ||
} | ||
] | ||
} |
Oops, something went wrong.