Skip to content

Commit c37a2e2

Browse files
aacebolilyydu
andauthored
[PY] feat: OAuth (microsoft#1517)
## Linked issues closes: microsoft#1317 ## Details OAuth feature implementation. --------- Co-authored-by: Lily Du <[email protected]>
1 parent 46c17f6 commit c37a2e2

File tree

117 files changed

+11461
-815
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

117 files changed

+11461
-815
lines changed

Diff for: python/packages/ai/.pylintrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,8 @@ disable=raw-checker-failed,
447447
too-many-boolean-expressions,
448448
too-many-arguments,
449449
too-many-instance-attributes,
450-
duplicate-code
450+
duplicate-code,
451+
redefined-outer-name
451452

452453
# Enable the message, report, category or checker with the given id(s). You can
453454
# either give multiple identifier separated by comma (,) or put this option

Diff for: python/packages/ai/poetry.lock

+680-622
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: python/packages/ai/pyproject.toml

+10-6
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,17 @@ jsonschema = "^4.21.1"
2323
types-pyyaml = "^6.0.12.12"
2424
pyyaml = "^6.0.1"
2525
dataclasses-json = "^0.6.4"
26-
openai = "^1.11.1"
2726
azure-ai-contentsafety = "^1.0.0"
27+
msal = "^1.28.0"
28+
botbuilder-dialogs = "^4.14.8"
29+
openai = "^1.27.0"
2830

2931
[tool.poetry.group.dev.dependencies]
30-
pytest = "^7.4.0"
31-
pylint = "^2.17.4"
32-
pytest-cov = "^4.1.0"
32+
pytest = "^8.1.1"
33+
pylint = "^3.1.0"
34+
pytest-cov = "^5.0.0"
3335
pytest-asyncio = "^0.21.1"
34-
black = ">=23.7,<25.0"
36+
black = "24.3.0"
3537
isort = "^5.12.0"
3638
mypy = "^1.5.0"
3739
httpx = "^0.26.0"
@@ -48,14 +50,16 @@ requires = ["poetry-core"]
4850
build-backend = "poetry.core.masonry.api"
4951

5052
[tool.black]
53+
preview = true
5154
line-length = 100
5255
target-version = ['py38']
56+
enable-unstable-feature = ['string_processing', 'multiline_string_handling']
5357

5458
[tool.isort]
5559
profile = "black"
5660

5761
[tool.pytest.ini_options]
58-
addopts = "-rx --cov-report html:coverage/html --cov-report lcov:coverage/lcov.info --cov=teams"
62+
addopts = "-rx --cov-report html:coverage/html --cov-report lcov:coverage/lcov.info --cov=teams --verbose"
5963
log_cli = true
6064
log_cli_level = "INFO"
6165

Diff for: python/packages/ai/teams/adaptive_cards/adaptive_cards.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,12 @@ def __selector__(context: TurnContext) -> bool:
8686
return False
8787

8888
def __call__(
89-
func: Callable[[TurnContext, StateT, dict], Awaitable[Union[str, dict]]]
89+
func: Callable[[TurnContext, StateT, dict], Awaitable[Union[str, dict]]],
9090
) -> Callable[[TurnContext, StateT, dict], Awaitable[Union[str, dict]]]:
9191
async def __handler__(context: TurnContext, state: StateT) -> bool:
9292
result = await func(context, state, context.activity.value["action"]["data"])
93-
if context.turn_state.get(ActivityTypes.invoke_response) is None:
93+
94+
if context.turn_state.get(context._INVOKE_RESPONSE_KEY) is None:
9495
if isinstance(result, str):
9596
response = AdaptiveCardInvokeResponse(
9697
status_code=200,
@@ -162,7 +163,7 @@ def __selector__(context: TurnContext) -> bool:
162163
return False
163164

164165
def __call__(
165-
func: Callable[[TurnContext, StateT, dict], Awaitable[None]]
166+
func: Callable[[TurnContext, StateT, dict], Awaitable[None]],
166167
) -> Callable[[TurnContext, StateT, dict], Awaitable[None]]:
167168
async def __handler__(context: TurnContext, state: StateT) -> bool:
168169
await func(context, state, context.activity.value)
@@ -229,7 +230,7 @@ def __call__(
229230
func: Callable[
230231
[TurnContext, StateT, Query[AdaptiveCardsSearchParams]],
231232
Awaitable[List[AdaptiveCardsSearchResult]],
232-
]
233+
],
233234
) -> Callable[
234235
[TurnContext, StateT, Query[AdaptiveCardsSearchParams]],
235236
Awaitable[List[AdaptiveCardsSearchResult]],
@@ -254,7 +255,7 @@ async def __handler__(context: TurnContext, state: StateT) -> bool:
254255
),
255256
)
256257
result = await func(context, state, query)
257-
if context.turn_state.get(ActivityTypes.invoke_response) is None:
258+
if context.turn_state.get(context._INVOKE_RESPONSE_KEY) is None:
258259
# Format invoke response
259260
response = {
260261
"type": "application/vnd.microsoft.search.searchResponse",

Diff for: python/packages/ai/teams/ai/ai.py

+4-8
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,10 @@ def __call__(func: ActionHandler[StateT]) -> ActionHandler[StateT]:
110110
existing = self._actions.get(action_name)
111111

112112
if existing and not existing.allow_overrides:
113-
raise ApplicationError(
114-
f"""
113+
raise ApplicationError(f"""
115114
The AI.action() method was called with a previously
116115
registered action named \"{action_name}\".
117-
"""
118-
)
116+
""")
119117

120118
self._actions[action_name] = ActionEntry[StateT](action_name, allow_overrides, func)
121119
return func
@@ -228,10 +226,8 @@ async def _on_http_error(
228226
status = _context.data.get("status")
229227
message = _context.data.get("message")
230228
raise ApplicationError(
231-
(
232-
"An AI request failed because an http error occurred. "
233-
f"Status code:{status}. Message:{message}"
234-
)
229+
"An AI request failed because an http error occurred. "
230+
f"Status code:{status}. Message:{message}"
235231
)
236232

237233
async def _on_plan_ready(

Diff for: python/packages/ai/teams/ai/augmentations/monologue_augmentation.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,22 @@ def __init__(self, actions: List[ChatCompletionAction]) -> None:
143143
call_to_action="\n".join(
144144
[
145145
"Return a JSON object with your thoughts and the next action to perform.",
146-
"Only respond with the JSON format below and base your plan on the actions above.",
147-
"If you're not sure what to do, you can always say something by returning a SAY action.",
146+
(
147+
"Only respond with the JSON format below and base your plan on the actions"
148+
" above."
149+
),
150+
(
151+
"If you're not sure what to do, you can always say something by returning a"
152+
" SAY action."
153+
),
148154
"If you're told your JSON response has errors, do your best to fix them.",
149155
"Response Format:",
150-
'{"thoughts":{"thought":"<your current thought>","reasoning":"<self reflect on why you made this decision>","plan":"- short bulleted\\n- list that conveys\\n- long-term plan"},"action":{"name":"<action name>","parameters":{"<name>":"<value>"}}}',
156+
(
157+
'{"thoughts":{"thought":"<your current thought>","reasoning":"<self reflect'
158+
' on why you made this decision>","plan":"- short bulleted\\n- list that'
159+
' conveys\\n- long-term plan"},"action":{"name":"<action'
160+
' name>","parameters":{"<name>":"<value>"}}}'
161+
),
151162
]
152163
),
153164
)

Diff for: python/packages/ai/teams/ai/embeddings/azure_openai_embeddings.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,10 @@ def __init__(self, options: AzureOpenAIEmbeddingsOptions, log=Logger("teams.ai")
5252
endpoint = endpoint[0 : (len(endpoint) - 1)]
5353

5454
if not endpoint.lower().startswith("https://"):
55-
raise ValueError(
56-
f"""
55+
raise ValueError(f"""
5756
Client created with an invalid endpoint of \"{endpoint}\".
5857
The endpoint must be a valid HTTPS url.
59-
"""
60-
)
58+
""")
6159

6260
self.options.azure_endpoint = endpoint
6361

Diff for: python/packages/ai/teams/ai/prompts/prompt_manager.py

+6-12
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,8 @@ def add_prompt(self, prompt: PromptTemplate) -> "PromptManager":
226226
"""
227227
if prompt.name in self._prompts:
228228
raise ApplicationError(
229-
(
230-
"The PromptManager.add_prompt() method was called with a "
231-
f"previously registered prompt named '{prompt.name}'."
232-
)
229+
"The PromptManager.add_prompt() method was called with a "
230+
f"previously registered prompt named '{prompt.name}'."
233231
)
234232

235233
# Clone and cache prompt
@@ -268,10 +266,8 @@ async def get_prompt(self, name: str) -> PromptTemplate:
268266
template_config = PromptTemplateConfig.from_dict(json.load(file))
269267
except Exception as e:
270268
raise ApplicationError(
271-
(
272-
"PromptManager.get_prompt(): an error occurred while loading "
273-
f"'{config_file}'. The file is either invalid or missing."
274-
)
269+
"PromptManager.get_prompt(): an error occurred while loading "
270+
f"'{config_file}'. The file is either invalid or missing."
275271
) from e
276272

277273
# Load prompt text
@@ -282,10 +278,8 @@ async def get_prompt(self, name: str) -> PromptTemplate:
282278
sections.append(TemplateSection(prompt, self._options.role))
283279
except Exception as e:
284280
raise ApplicationError(
285-
(
286-
"PromptManager.get_prompt(): an error occurred while loading "
287-
f"'{prompt_file}'. The file is either invalid or missing."
288-
)
281+
"PromptManager.get_prompt(): an error occurred while loading "
282+
f"'{prompt_file}'. The file is either invalid or missing."
289283
) from e
290284

291285
# Load optional actions

Diff for: python/packages/ai/teams/ai/validators/action_response_validator.py

+15-9
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,9 @@ async def validate_response(
9494

9595
return Validation(
9696
valid=False,
97-
feedback=f"No {self._noun} was specified. "
98-
f"Call a {self._noun} with valid arguments.",
97+
feedback=(
98+
f"No {self._noun} was specified. Call a {self._noun} with valid arguments."
99+
),
99100
)
100101

101102
if func.name is None:
@@ -107,8 +108,9 @@ async def validate_response(
107108
if not func.name in self._actions:
108109
return Validation(
109110
valid=False,
110-
feedback=f'Unknown {self._noun} named "{func.name}". '
111-
f"Specify a valid {self._noun} name.",
111+
feedback=(
112+
f'Unknown {self._noun} named "{func.name}". Specify a valid {self._noun} name.'
113+
),
112114
)
113115

114116
params: Dict[str, Any] = {}
@@ -117,11 +119,15 @@ async def validate_response(
117119
if action.parameters is not None:
118120
validator = JSONResponseValidator(
119121
schema=action.parameters,
120-
missing_json_feedback=f"No arguments were sent with called {self._noun}. "
121-
f'Call the "{func.name}" {self._noun} with required '
122-
f"arguments as a valid JSON object.",
123-
error_feedback=f"The {self._noun} arguments had errors. "
124-
f'Apply these fixes and call "{func.name}" {self._noun} again:',
122+
missing_json_feedback=(
123+
f"No arguments were sent with called {self._noun}. "
124+
f'Call the "{func.name}" {self._noun} with required '
125+
"arguments as a valid JSON object."
126+
),
127+
error_feedback=(
128+
f"The {self._noun} arguments had errors. "
129+
f'Apply these fixes and call "{func.name}" {self._noun} again:'
130+
),
125131
)
126132

127133
res = await validator.validate_response(

0 commit comments

Comments
 (0)