Skip to content

Commit 6c60156

Browse files
authored
Merge branch 'main' into dependabot/nuget/dotnet/packages/Microsoft.TeamsAI/production-32a9be39d8
2 parents ec08168 + 321cd93 commit 6c60156

35 files changed

+757
-211
lines changed

.github/workflows/coverage.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Checkout
3535
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
3636
- name: Setup NodeJS ${{ env.node-version }}
37-
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
37+
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
3838
with:
3939
node-version: ${{ env.node-version }}
4040
- name: Install Dependencies

.github/workflows/dotnet-codeql.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
- name: Checkout
4040
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
4141
- name: Initialize CodeQL
42-
uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
42+
uses: github/codeql-action/init@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
4343
with:
4444
languages: csharp
4545
- name: Setup .NET
@@ -50,6 +50,6 @@ jobs:
5050
working-directory: dotnet/packages/Microsoft.TeamsAI/
5151
run: dotnet build Microsoft.Teams.AI.sln --configuration Release
5252
- name: Perform CodeQL Analysis
53-
uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
53+
uses: github/codeql-action/analyze@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
5454
with:
5555
category: "/language:csharp"

.github/workflows/js-build-test-lint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- name: Checkout
3131
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
3232
- name: Setup NodeJS ${{ matrix.node-version }}
33-
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
33+
uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
3434
with:
3535
node-version: ${{ matrix.node-version }}
3636
- name: Install Dependencies

.github/workflows/js-codeql.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ jobs:
3838
- name: Checkout
3939
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
4040
- name: Initialize CodeQL
41-
uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
41+
uses: github/codeql-action/init@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
4242
with:
4343
languages: javascript
4444
- name: Perform CodeQL Analysis
45-
uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
45+
uses: github/codeql-action/analyze@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
4646
with:
4747
category: "/language:javascript"

.github/workflows/python-codeql.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@ jobs:
3838
- name: Checkout
3939
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
4040
- name: Initialize CodeQL
41-
uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
41+
uses: github/codeql-action/init@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
4242
with:
4343
languages: python
4444
- name: Perform CodeQL Analysis
45-
uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
45+
uses: github/codeql-action/analyze@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
4646
with:
4747
category: "/language:python"

.github/workflows/scorecards.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,6 @@ jobs:
6666

6767
# Upload the results to GitHub's code scanning dashboard.
6868
- name: "Upload to code-scanning"
69-
uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
69+
uses: github/codeql-action/upload-sarif@f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4 # v3.28.5
7070
with:
7171
sarif_file: results.sarif

getting-started/CONCEPTS/STREAMING.md

+2
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ There are three parts to streaming:
3535

3636
## Sample Bots
3737
- [C# Streaming ChefBot](https://github.com/microsoft/teams-ai/tree/main/dotnet/samples/04.ai.g.teamsChefBot-streaming)
38+
- [C# Streaming + Tools LightBot](https://github.com/microsoft/teams-ai/blob/main/dotnet/samples/04.ai.c.actionMapping.lightBot)
3839
- [JS Streaming ChefBot](https://github.com/microsoft/teams-ai/tree/main/js/samples/04.ai-apps/i.teamsChefBot-streaming)
3940
- [JS Streaming+Tools LightBot](https://github.com/microsoft/teams-ai/tree/main/js/samples/03.ai-concepts/c.actionMapping-lightBot)
4041
- [Python Streaming ListBot](https://github.com/microsoft/teams-ai/tree/main/python/samples/04.ai.h.chainedActions.listBot-streaming)
42+
- [Python Streaming+Tools LightBot](https://github.com/microsoft/teams-ai/tree/main/python/samples/04.ai.c.actionMapping.lightBot)
4143

4244
## Streaming Response Class
4345
The `StreamingResponse` class is the helper class for streaming responses to the client. The class is used to send a series of updates to the client in a single response. If you are using your own custom model, you can directly instantiate and manage this class to stream responses.

js/packages/teams-ai/src/Application.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -1021,14 +1021,18 @@ export class Application<TState extends TurnState = TurnState> {
10211021
public startTypingTimer(context: TurnContext): void {
10221022
if (context.activity.type == ActivityTypes.Message && !this._typingTimer) {
10231023
// Listen for outgoing activities
1024-
context.onSendActivities((context, activities, next) => {
1024+
context.onSendActivities(async (context, activities, next) => {
10251025
// Listen for any messages to be sent from the bot
10261026
if (timerRunning) {
10271027
for (let i = 0; i < activities.length; i++) {
10281028
if (activities[i].type == ActivityTypes.Message || activities[i].channelData?.streamType) {
10291029
// Stop the timer
10301030
this.stopTypingTimer();
10311031
timerRunning = false;
1032+
1033+
// Wait for the last "typing" activity to finish sending
1034+
// - This prevents a race condition that results in the typing indicator being stuck on.
1035+
await lastSend;
10321036
break;
10331037
}
10341038
}
@@ -1038,17 +1042,20 @@ export class Application<TState extends TurnState = TurnState> {
10381042
});
10391043

10401044
let timerRunning = true;
1045+
let lastSend: Promise<any> = Promise.resolve();
10411046
const onTimeout = async () => {
10421047
try {
10431048
// Send typing activity
1044-
await context.sendActivity({ type: ActivityTypes.Typing });
1049+
lastSend = context.sendActivity({ type: ActivityTypes.Typing });
1050+
await lastSend;
10451051
} catch (err) {
10461052
// Seeing a random proxy violation error from the context object. This is because
10471053
// we're in the middle of sending an activity on a background thread when the turn ends.
10481054
// The context object throws when we try to update "this.responded = true". We can just
10491055
// eat the error but lets make sure our states cleaned up a bit.
10501056
this._typingTimer = undefined;
10511057
timerRunning = false;
1058+
lastSend = Promise.resolve();
10521059
}
10531060

10541061
// Restart timer

python/packages/ai/poetry.lock

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

python/packages/ai/teams/ai/ai.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ async def _on_say_command(
301301
for i, citation in enumerate(msg_context.citations):
302302
citations.append(
303303
ClientCitation(
304-
position=f"{i + 1}",
304+
position=i + 1,
305305
appearance=Appearance(
306306
name=citation.title or f"Document {i + 1}",
307307
abstract=snippet(citation.content, 477),
@@ -315,10 +315,14 @@ async def _on_say_command(
315315

316316
# If there are citations, filter out the citations unused in content.
317317
referenced_citations = get_used_citations(content_text, citations)
318-
channel_data = {}
318+
channel_data: Dict[str, Any] = {}
319319

320320
if is_teams_channel:
321-
channel_data["feedbackLoopEnabled"] = self._options.enable_feedback_loop
321+
if self._options.enable_feedback_loop and not self._options.feedback_loop_type:
322+
channel_data["feedbackLoopEnabled"] = self._options.enable_feedback_loop
323+
324+
if self._options.feedback_loop_type:
325+
channel_data["feedbackLoop"] = {"type": self._options.feedback_loop_type}
322326

323327
await context.send_activity(
324328
Activity(

python/packages/ai/teams/ai/ai_options.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from __future__ import annotations
77

88
from dataclasses import dataclass, field
9-
from typing import Generic, TypeVar
9+
from typing import Generic, Literal, Optional, TypeVar
1010

1111
from ..state import TurnState
1212
from .moderators.default_moderator import DefaultModerator
@@ -40,3 +40,5 @@ class AIOptions(Generic[StateT]):
4040
Optional. If true, the AI system will enable the feedback loop in Teams that
4141
allows a user to give thumbs up or down to a response.
4242
"""
43+
44+
feedback_loop_type: Optional[Literal["default", "custom"]] = None

python/packages/ai/teams/ai/citations/__init__.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@
66
from .citations import (
77
AIEntity,
88
Appearance,
9+
AppearanceImage,
910
ClientCitation,
11+
ClientCitationIconName,
1012
Pattern,
1113
SensitivityUsageInfo,
1214
)
1315

14-
__all__ = ["ClientCitation", "Appearance", "SensitivityUsageInfo", "Pattern", "AIEntity"]
16+
__all__ = [
17+
"ClientCitation",
18+
"ClientCitationIconName",
19+
"Appearance",
20+
"AppearanceImage",
21+
"SensitivityUsageInfo",
22+
"Pattern",
23+
"AIEntity",
24+
]

python/packages/ai/teams/ai/citations/citations.py

+57-10
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from __future__ import annotations
77

88
from dataclasses import dataclass, field
9-
from typing import Optional
9+
from typing import Literal, Optional, Union
1010

1111
from botbuilder.schema import Entity
1212
from msrest.serialization import Model
@@ -47,11 +47,11 @@ class ClientCitation(Model):
4747

4848
_attribute_map = {
4949
"type_": {"key": "@type", "type": "str"},
50-
"position": {"key": "position", "type": "str"},
50+
"position": {"key": "position", "type": "int"},
5151
"appearance": {"key": "appearance", "type": "Appearance"},
5252
}
5353

54-
position: str
54+
position: int
5555
appearance: Appearance
5656
type_: str = field(default="Claim", metadata={"alias": "@type"}, init=False, repr=False)
5757

@@ -63,13 +63,13 @@ class Appearance(Model):
6363
6464
Attributes:
6565
@type (str): Required; must be 'DigitalDocument'
66-
name (str): The name of the document
66+
name (str): The name of the document. (max length 80)
6767
text (str): Optional; the appearance text of the citation.
6868
url (str): The url of the document
69-
abstract (str): Content of the citation. Must be clipped if longer than 480 characters
69+
abstract (str): Extract of the referenced content. (max length 160)
7070
encodingFormat (str): Encoding format of the `citation.appearance.text` field.
71-
image (str): Used for icon; for now it is ignored
72-
keywords (list[str]): The optional keywords to the citation
71+
image (AppearanceImage): Information about the citation’s icon.
72+
keywords (list[str]): Optional; set by developer. (max length 3) (max keyword length 28)
7373
usageInfo (SensitivityUsageInfo): The optional sensitivity content information
7474
"""
7575

@@ -82,20 +82,44 @@ class Appearance(Model):
8282
"text": {"key": "text", "type": "str"},
8383
"url": {"key": "url", "type": "str"},
8484
"encoding_format": {"key": "encodingFormat", "type": "str"},
85-
"image": {"key": "image", "type": "str"},
85+
"image": {"key": "image", "type": "AppearanceImage"},
8686
}
8787

8888
name: str
8989
abstract: str
9090
keywords: Optional[list[str]] = field(default=None)
9191
text: Optional[str] = field(default=None)
9292
url: Optional[str] = field(default=None)
93-
image: Optional[str] = field(default=None)
94-
encoding_format: Optional[str] = field(default=None)
93+
image: Optional[AppearanceImage] = field(default=None)
94+
encoding_format: Optional[
95+
Union[
96+
Literal["text/html"],
97+
Literal["application/vnd.microsoft.card.adaptive"],
98+
]
99+
] = field(default=None)
95100
usage_info: Optional[SensitivityUsageInfo] = field(default=None)
96101
type_: str = field(default="DigitalDocument", metadata={"alias": "@type"})
97102

98103

104+
@dataclass
105+
class AppearanceImage(Model):
106+
"""
107+
Represents how the citation will be rendered
108+
109+
Attributes:
110+
@type (str): Required; must be 'ImageObject'
111+
name (str): The image/icon name
112+
"""
113+
114+
_attribute_map = {
115+
"type_": {"key": "@type", "type": "str"},
116+
"name": {"key": "name", "type": "str"},
117+
}
118+
119+
name: ClientCitationIconName
120+
type_: str = field(default="ImageObject", metadata={"alias": "@type"})
121+
122+
99123
@dataclass
100124
class SensitivityUsageInfo(Model):
101125
"""
@@ -144,3 +168,26 @@ class Pattern(Model):
144168
name: str
145169
term_code: str
146170
type_: str = field(default="DefinedTerm", metadata={"alias": "@type"})
171+
172+
173+
ClientCitationIconName = Union[
174+
Literal["Microsoft Workd"],
175+
Literal["Microsoft Excel"],
176+
Literal["Microsoft PowerPoint"],
177+
Literal["Microsoft Visio"],
178+
Literal["Microsoft Loop"],
179+
Literal["Microsoft Whiteboard"],
180+
Literal["Adobe Illustrator"],
181+
Literal["Adobe Photoshop"],
182+
Literal["Adobe InDesign"],
183+
Literal["Adobe Flash"],
184+
Literal["Sketch"],
185+
Literal["Source Code"],
186+
Literal["Image"],
187+
Literal["GIF"],
188+
Literal["Video"],
189+
Literal["Sound"],
190+
Literal["ZIP"],
191+
Literal["Text"],
192+
Literal["PDF"],
193+
]

0 commit comments

Comments
 (0)