Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c7423e8
Fastapi integration draft
axelsrz Oct 16, 2025
1a6aa8d
Potential fix for code scanning alert no. 11: Information exposure th…
axelsrz Oct 16, 2025
0a6d895
Update libraries/microsoft-agents-hosting-fastapi/microsoft_agents/ho…
axelsrz Oct 16, 2025
8b3f33d
Merge branch 'main' of https://github.com/microsoft/Agents-for-python…
axelsrz Oct 20, 2025
57eccd8
Fastapi integration wip
axelsrz Oct 20, 2025
3d4c0fe
Fastapi integration wip, emtpy agent working
axelsrz Oct 22, 2025
bf4a0d1
Merge branch 'main' of https://github.com/microsoft/Agents-for-python…
axelsrz Oct 24, 2025
619dd4a
Fastapi integration wip, auth agent bugfixing
axelsrz Oct 24, 2025
e7f0cad
Revert "1. TypingIndicator Concurrency & Argument fixes (#187)"
axelsrz Oct 24, 2025
c72a8eb
Fastapi integration tentative, need to validate auth behavior
axelsrz Oct 24, 2025
268d523
Fastapi integration tentative, need to validate auth behavior: nit fix
axelsrz Oct 24, 2025
1e0a306
Reapply "1. TypingIndicator Concurrency & Argument fixes (#187)"
axelsrz Oct 27, 2025
c0807d6
Update test_samples/fastapi/README.md
axelsrz Oct 27, 2025
36bf460
Update test_samples/fastapi/shared/__init__.py
axelsrz Oct 27, 2025
8174a78
Update test_samples/fastapi/shared/github_api_client.py
axelsrz Oct 27, 2025
e4784cf
Update test_samples/fastapi/authorization_agent.py
axelsrz Oct 27, 2025
19cf97c
Fastapi integration tentative, need to validate auth behavior: copilo…
axelsrz Oct 27, 2025
cb48382
Fastapi integration tentative, need to validate auth behavior: resolv…
axelsrz Oct 27, 2025
51e7db4
Merge branch 'main' into users/axsuarez/fast-api-integration
axelsrz Oct 27, 2025
5e948bc
Update test_samples/fastapi/shared/github_api_client.py
axelsrz Oct 27, 2025
6b9f0f5
Update libraries/microsoft-agents-hosting-fastapi/microsoft_agents/ho…
axelsrz Oct 27, 2025
7c3d1af
Update test_samples/fastapi/shared/cards.py
axelsrz Oct 27, 2025
a72346c
Fastapi integration tentative, need to validate auth behavior: nit fix
axelsrz Oct 27, 2025
64fe22e
Merge branch 'users/axsuarez/fast-api-integration' of https://github.…
axelsrz Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion DevInstructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ Any issues should be filed on the [Agents-for-python](https://github.com/microso
pip install -e ./libraries/microsoft-agents-hosting-aiohttp/ --config-settings editable_mode=compat
pip install -e ./libraries/microsoft-agents-hosting-teams/ --config-settings editable_mode=compat
pip install -e ./libraries/microsoft-agents-storage-blob/ --config-settings editable_mode=compat
pip install -e ./libraries/microsoft-agents-storage-cosmos/ --config-settings editable_mode=compat
pip install -e ./libraries/microsoft-agents-storage-cosmos/ --config-settings editable_mode=compat
pip install -e ./libraries/microsoft-agents-hosting-fastapi/ --config-settings editable_mode=compat
```
1. Setup the dev dependencies for python. In the terminal, at the project root, run:
```
Expand Down
21 changes: 21 additions & 0 deletions libraries/microsoft-agents-hosting-fastapi/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Microsoft Corporation.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from ._start_agent_process import start_agent_process
from .agent_http_adapter import AgentHttpAdapter
from .channel_service_route_table import channel_service_route_table
from .cloud_adapter import CloudAdapter
from .jwt_authorization_middleware import (
JwtAuthorizationMiddleware,
)
from .app.streaming import (
Citation,
CitationUtil,
StreamingResponse,
)

__all__ = [
"start_agent_process",
"AgentHttpAdapter",
"CloudAdapter",
"JwtAuthorizationMiddleware",
"channel_service_route_table",
"Citation",
"CitationUtil",
"StreamingResponse",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import Optional
from fastapi import Request, Response
from microsoft_agents.hosting.core.app import AgentApplication
from .cloud_adapter import CloudAdapter


async def start_agent_process(
request: Request,
agent_application: AgentApplication,
adapter: CloudAdapter,
) -> Optional[Response]:
"""Starts the agent host with the provided adapter and agent application.
Args:
adapter (CloudAdapter): The adapter to use for the agent host.
agent_application (AgentApplication): The agent application to run.
"""
if not adapter:
raise TypeError("start_agent_process: adapter can't be None")
if not agent_application:
raise TypeError("start_agent_process: agent_application can't be None")

# Start the agent application with the provided adapter
return await adapter.process(
request,
agent_application,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from abc import abstractmethod
from typing import Optional, Protocol

from fastapi import Request, Response

from microsoft_agents.hosting.core import Agent


class AgentHttpAdapter(Protocol):
@abstractmethod
async def process(self, request: Request, agent: Agent) -> Optional[Response]:
raise NotImplementedError()
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from .streaming import (
Citation,
CitationUtil,
StreamingResponse,
)

__all__ = [
"Citation",
"CitationUtil",
"StreamingResponse",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from .citation import Citation
from .citation_util import CitationUtil
from .streaming_response import StreamingResponse

__all__ = [
"Citation",
"CitationUtil",
"StreamingResponse",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

from typing import Optional
from dataclasses import dataclass


@dataclass
class Citation:
"""Citations returned by the model."""

content: str
"""The content of the citation."""

title: Optional[str] = None
"""The title of the citation."""

url: Optional[str] = None
"""The URL of the citation."""

filepath: Optional[str] = None
"""The filepath of the document."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import re
from typing import List, Optional

from microsoft_agents.activity import ClientCitation


class CitationUtil:
"""Utility functions for manipulating text and citations."""

@staticmethod
def snippet(text: str, max_length: int) -> str:
"""
Clips the text to a maximum length in case it exceeds the limit.

Args:
text: The text to clip.
max_length: The maximum length of the text to return, cutting off the last whole word.

Returns:
The modified text
"""
if len(text) <= max_length:
return text

snippet = text[:max_length]
snippet = snippet[: min(len(snippet), snippet.rfind(" "))]
snippet += "..."
return snippet

@staticmethod
def format_citations_response(text: str) -> str:
"""
Convert citation tags `[doc(s)n]` to `[n]` where n is a number.

Args:
text: The text to format.

Returns:
The formatted text.
"""
return re.sub(r"\[docs?(\d+)\]", r"[\1]", text, flags=re.IGNORECASE)

@staticmethod
def get_used_citations(
text: str, citations: List[ClientCitation]
) -> Optional[List[ClientCitation]]:
"""
Get the citations used in the text. This will remove any citations that are
included in the citations array from the response but not referenced in the text.

Args:
text: The text to search for citation references, i.e. [1], [2], etc.
citations: The list of citations to search for.

Returns:
The list of citations used in the text.
"""
regex = re.compile(r"\[(\d+)\]", re.IGNORECASE)
matches = regex.findall(text)

if not matches:
return None

# Remove duplicates
filtered_matches = set(matches)

# Add citations
used_citations = []
for match in filtered_matches:
citation_ref = f"[{match}]"
found = next(
(
citation
for citation in citations
if f"[{citation.position}]" == citation_ref
),
None,
)
if found:
used_citations.append(found)

return used_citations if used_citations else None
Loading