Skip to content

Commit

Permalink
Merge branch 'main' into lilyydu/migration-docs-for-pp
Browse files Browse the repository at this point in the history
  • Loading branch information
lilyydu committed Feb 27, 2024
2 parents a5e994c + 86192b5 commit 32399b1
Show file tree
Hide file tree
Showing 15 changed files with 272 additions and 230 deletions.
2 changes: 0 additions & 2 deletions python/packages/ai/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ Welcome to the Teams AI Library Python package!

This SDK is specifically designed to assist you in creating bots capable of interacting with Teams and Microsoft 365 applications. It is constructed using the [Bot Framework SDK](https://github.com/microsoft/botbuilder-python) as its foundation, simplifying the process of developing bots that interact with Teams' artificial intelligence capabilities. See the [Teams AI repo README.md](https://github.com/microsoft/teams-ai), for general information.

To get started with the SDK [see](../../../getting-started/README.md).

## Getting Started

To get started, take a look at the [getting started docs](https://github.com/microsoft/teams-ai/blob/main/getting-started/README.md).
Expand Down
2 changes: 1 addition & 1 deletion python/packages/ai/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "teams-ai"
version = "1.0.0.rc0"
version = "1.0.0.rc1"
description = "SDK focused on building AI based applications for Microsoft Teams."
authors = ["Microsoft <[email protected]>"]
readme = "README.md"
Expand Down
2 changes: 2 additions & 0 deletions python/packages/ai/teams/adaptive_cards/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
from .adaptive_cards_options import AdaptiveCardsOptions
from .adaptive_cards_search_params import AdaptiveCardsSearchParams
from .adaptive_cards_search_result import AdaptiveCardsSearchResult

__all__ = ["AdaptiveCardsOptions", "AdaptiveCardsSearchParams", "AdaptiveCardsSearchResult"]
42 changes: 22 additions & 20 deletions python/packages/ai/teams/adaptive_cards/adaptive_cards.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from __future__ import annotations

import re
from typing import Awaitable, Callable, List, Pattern, Union
from typing import Awaitable, Callable, Generic, List, Pattern, TypeVar, Union

from botbuilder.core import TurnContext
from botbuilder.schema import (
Expand All @@ -27,8 +27,10 @@
ACTION_EXECUTE_TYPE = "Action.Execute"
SEARCH_INVOKE_NAME = "application/search"

StateT = TypeVar("StateT", bound=TurnState)

class AdaptiveCards:

class AdaptiveCards(Generic[StateT]):
_route_registry: List[Route]
_action_submit_filter: str

Expand All @@ -39,8 +41,8 @@ def __init__(self, route_registry: List[Route], action_submit_filter: str) -> No
def action_execute(
self, verb: Union[str, Pattern[str], Callable[[TurnContext], bool]]
) -> Callable[
[Callable[[TurnContext, TurnState, dict], Awaitable[Union[str, dict]]]],
Callable[[TurnContext, TurnState, dict], Awaitable[Union[str, dict]]],
[Callable[[TurnContext, StateT, dict], Awaitable[Union[str, dict]]]],
Callable[[TurnContext, StateT, dict], Awaitable[Union[str, dict]]],
]:
"""
Adds a route for handling Adaptive Card Action.Execute events.
Expand Down Expand Up @@ -84,9 +86,9 @@ def __selector__(context: TurnContext) -> bool:
return False

def __call__(
func: Callable[[TurnContext, TurnState, dict], Awaitable[Union[str, dict]]]
) -> Callable[[TurnContext, TurnState, dict], Awaitable[Union[str, dict]]]:
async def __handler__(context: TurnContext, state: TurnState) -> bool:
func: Callable[[TurnContext, StateT, dict], Awaitable[Union[str, dict]]]
) -> Callable[[TurnContext, StateT, dict], Awaitable[Union[str, dict]]]:
async def __handler__(context: TurnContext, state: StateT) -> bool:
result = await func(context, state, context.activity.value["action"]["data"])
if context.turn_state.get(ActivityTypes.invoke_response) is None:
if isinstance(result, str):
Expand All @@ -108,16 +110,16 @@ async def __handler__(context: TurnContext, state: TurnState) -> bool:
)
return True

self._route_registry.append(Route(__selector__, __handler__))
self._route_registry.append(Route[StateT](__selector__, __handler__))
return func

return __call__

def action_submit(
self, verb: Union[str, Pattern[str], Callable[[TurnContext], bool]]
) -> Callable[
[Callable[[TurnContext, TurnState, dict], Awaitable[None]]],
Callable[[TurnContext, TurnState, dict], Awaitable[None]],
[Callable[[TurnContext, StateT, dict], Awaitable[None]]],
Callable[[TurnContext, StateT, dict], Awaitable[None]],
]:
"""
Adds a route for handling Adaptive Card Action.Submit events.
Expand Down Expand Up @@ -160,13 +162,13 @@ def __selector__(context: TurnContext) -> bool:
return False

def __call__(
func: Callable[[TurnContext, TurnState, dict], Awaitable[None]]
) -> Callable[[TurnContext, TurnState, dict], Awaitable[None]]:
async def __handler__(context: TurnContext, state: TurnState) -> bool:
func: Callable[[TurnContext, StateT, dict], Awaitable[None]]
) -> Callable[[TurnContext, StateT, dict], Awaitable[None]]:
async def __handler__(context: TurnContext, state: StateT) -> bool:
await func(context, state, context.activity.value)
return True

self._route_registry.append(Route(__selector__, __handler__))
self._route_registry.append(Route[StateT](__selector__, __handler__))
return func

return __call__
Expand All @@ -176,12 +178,12 @@ def search(
) -> Callable[
[
Callable[
[TurnContext, TurnState, Query[AdaptiveCardsSearchParams]],
[TurnContext, StateT, Query[AdaptiveCardsSearchParams]],
Awaitable[List[AdaptiveCardsSearchResult]],
]
],
Callable[
[TurnContext, TurnState, Query[AdaptiveCardsSearchParams]],
[TurnContext, StateT, Query[AdaptiveCardsSearchParams]],
Awaitable[List[AdaptiveCardsSearchResult]],
],
]:
Expand Down Expand Up @@ -227,14 +229,14 @@ def __selector__(context: TurnContext) -> bool:

def __call__(
func: Callable[
[TurnContext, TurnState, Query[AdaptiveCardsSearchParams]],
[TurnContext, StateT, Query[AdaptiveCardsSearchParams]],
Awaitable[List[AdaptiveCardsSearchResult]],
]
) -> Callable[
[TurnContext, TurnState, Query[AdaptiveCardsSearchParams]],
[TurnContext, StateT, Query[AdaptiveCardsSearchParams]],
Awaitable[List[AdaptiveCardsSearchResult]],
]:
async def __handler__(context: TurnContext, state: TurnState) -> bool:
async def __handler__(context: TurnContext, state: StateT) -> bool:
params = context.activity.value
# Flatten search parameters
query = Query[AdaptiveCardsSearchParams](
Expand Down Expand Up @@ -263,7 +265,7 @@ async def __handler__(context: TurnContext, state: TurnState) -> bool:
)
return True

self._route_registry.append(Route(__selector__, __handler__))
self._route_registry.append(Route[StateT](__selector__, __handler__))
return func

return __call__
11 changes: 6 additions & 5 deletions python/packages/ai/teams/ai/actions/action_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@

from __future__ import annotations

from typing import Any, Awaitable, Callable, Optional
from typing import Any, Awaitable, Callable, Generic, Optional, TypeVar

from botbuilder.core import TurnContext

from teams.state import TurnState

from .action_turn_context import ActionTurnContext

ActionHandler = Callable[[ActionTurnContext, TurnState], Awaitable[str]]
StateT = TypeVar("StateT", bound=TurnState)
ActionHandler = Callable[[ActionTurnContext, StateT], Awaitable[str]]


class ActionEntry:
class ActionEntry(Generic[StateT]):
name: str
allow_overrides: bool
func: ActionHandler
func: ActionHandler[StateT]

def __init__(
self,
Expand All @@ -32,6 +33,6 @@ def __init__(
self.func = func

async def invoke(
self, context: TurnContext, state: TurnState, data: Any, name: Optional[str] = None
self, context: TurnContext, state: StateT, data: Any, name: Optional[str] = None
) -> str:
return await self.func(ActionTurnContext(name or self.name, data, context), state)
50 changes: 29 additions & 21 deletions python/packages/ai/teams/ai/ai.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from datetime import datetime
from logging import Logger
from typing import Callable, Dict, Optional
from typing import Callable, Dict, Generic, Optional, TypeVar

from botbuilder.core import TurnContext
from botframework.connector import Channels
Expand All @@ -20,8 +20,10 @@
from .planners.plan import Plan, PredictedDoCommand, PredictedSayCommand
from .planners.planner import Planner

StateT = TypeVar("StateT", bound=TurnState)

class AI:

class AI(Generic[StateT]):
"""
### AI System
Expand All @@ -31,7 +33,7 @@ class AI:

_options: AIOptions
_logger: Logger
_actions: Dict[str, ActionEntry] = {}
_actions: Dict[str, ActionEntry[StateT]] = {}

@property
def options(self) -> AIOptions:
Expand All @@ -49,26 +51,32 @@ def __init__(self, options: AIOptions, *, logger=Logger("teams.ai")) -> None:
self._options = options
self._logger = logger
self._actions = {
ActionTypes.UNKNOWN_ACTION: ActionEntry(
ActionTypes.UNKNOWN_ACTION: ActionEntry[StateT](
ActionTypes.UNKNOWN_ACTION, True, self._on_unknown_action
),
ActionTypes.FLAGGED_INPUT: ActionEntry(
ActionTypes.FLAGGED_INPUT: ActionEntry[StateT](
ActionTypes.FLAGGED_INPUT, True, self._on_flagged_input
),
ActionTypes.FLAGGED_OUTPUT: ActionEntry(
ActionTypes.FLAGGED_OUTPUT: ActionEntry[StateT](
ActionTypes.FLAGGED_OUTPUT, True, self._on_flagged_output
),
ActionTypes.HTTP_ERROR: ActionEntry(ActionTypes.HTTP_ERROR, True, self._on_http_error),
ActionTypes.PLAN_READY: ActionEntry(ActionTypes.PLAN_READY, True, self._on_plan_ready),
ActionTypes.DO_COMMAND: ActionEntry(ActionTypes.DO_COMMAND, True, self._on_do_command),
ActionTypes.SAY_COMMAND: ActionEntry(
ActionTypes.HTTP_ERROR: ActionEntry[StateT](
ActionTypes.HTTP_ERROR, True, self._on_http_error
),
ActionTypes.PLAN_READY: ActionEntry[StateT](
ActionTypes.PLAN_READY, True, self._on_plan_ready
),
ActionTypes.DO_COMMAND: ActionEntry[StateT](
ActionTypes.DO_COMMAND, True, self._on_do_command
),
ActionTypes.SAY_COMMAND: ActionEntry[StateT](
ActionTypes.SAY_COMMAND, True, self._on_say_command
),
}

def action(
self, name: Optional[str] = None, *, allow_overrides=False
) -> Callable[[ActionHandler], ActionHandler]:
) -> Callable[[ActionHandler[StateT]], ActionHandler[StateT]]:
"""
Registers a new action event listener. This method can be used as either
a decorator or a method.
Expand All @@ -90,7 +98,7 @@ async def hello_world(context: TurnContext, state: TurnState, entities: Any, nam
are found `Default: False`
"""

def __call__(func: ActionHandler) -> ActionHandler:
def __call__(func: ActionHandler[StateT]) -> ActionHandler[StateT]:
action_name = name

if not action_name:
Expand All @@ -106,15 +114,15 @@ def __call__(func: ActionHandler) -> ActionHandler:
"""
)

self._actions[action_name] = ActionEntry(action_name, allow_overrides, func)
self._actions[action_name] = ActionEntry[StateT](action_name, allow_overrides, func)
return func

return __call__

async def run(
self,
context: TurnContext,
state: TurnState,
state: StateT,
started_at: datetime = datetime.now(),
step: int = 0,
) -> bool:
Expand Down Expand Up @@ -182,7 +190,7 @@ async def run(
async def _on_unknown_action(
self,
context: ActionTurnContext,
_state: TurnState,
_state: StateT,
) -> str:
self._logger.error(
'An AI action named "%s" was predicted but no handler was registered', context.name
Expand All @@ -192,7 +200,7 @@ async def _on_unknown_action(
async def _on_flagged_input(
self,
context: ActionTurnContext,
_state: TurnState,
_state: StateT,
) -> str:
self._logger.error(
"The users input has been moderated but no handler was registered for %s", context.name
Expand All @@ -202,7 +210,7 @@ async def _on_flagged_input(
async def _on_flagged_output(
self,
context: ActionTurnContext,
_state: TurnState,
_state: StateT,
) -> str:
self._logger.error(
"The apps output has been moderated but no handler was registered for %s", context.name
Expand All @@ -212,7 +220,7 @@ async def _on_flagged_output(
async def _on_http_error(
self,
_context: ActionTurnContext[dict],
_state: TurnState,
_state: StateT,
) -> str:
status = _context.data.get("status")
message = _context.data.get("message")
Expand All @@ -226,14 +234,14 @@ async def _on_http_error(
async def _on_plan_ready(
self,
context: ActionTurnContext[Plan],
_state: TurnState,
_state: StateT,
) -> str:
return "" if len(context.data.commands) > 0 else ActionTypes.STOP

async def _on_do_command(
self,
context: ActionTurnContext[PredictedDoCommand],
state: TurnState,
state: StateT,
) -> str:
action = self._actions.get(context.data.action)
ctx = ActionTurnContext(context.data.action, context.data.parameters, context)
Expand All @@ -246,7 +254,7 @@ async def _on_do_command(
async def _on_say_command(
self,
context: ActionTurnContext[PredictedSayCommand],
_state: TurnState,
_state: StateT,
) -> str:
response = context.data.response

Expand Down
4 changes: 1 addition & 3 deletions python/packages/ai/teams/ai/prompts/prompt_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,7 @@ def options(self) -> PromptManagerOptions:
"""
return self._options

def function(
self, name: Optional[str] = None
) -> Callable[[PromptFunction], PromptFunction,]:
def function(self, name: Optional[str] = None) -> Callable[[PromptFunction], PromptFunction]:
"""
Registers a new prompt function event listener. This method can be used as either
a decorator or a method.
Expand Down
Loading

0 comments on commit 32399b1

Please sign in to comment.