mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Wrapper + Samples 1st (#5177)
* Experiment * Update dependency and add non streaming * Add more samples * Rename samples * Add invocations * Comments 1 * Comments 2 * Comments 3 * Improve README * Add local shell sample * WIP: Add eval and memory samples * Update user agent prefix * Update user agent prefix doc
This commit is contained in:
committed by
GitHub
Unverified
parent
7010dd7439
commit
615ef9049f
@@ -26,6 +26,28 @@ USER_AGENT_KEY: Final[str] = "User-Agent"
|
||||
HTTP_USER_AGENT: Final[str] = "agent-framework-python"
|
||||
AGENT_FRAMEWORK_USER_AGENT = f"{HTTP_USER_AGENT}/{version_info}" # type: ignore[has-type]
|
||||
|
||||
_user_agent_prefixes: list[str] = []
|
||||
|
||||
|
||||
def append_to_user_agent(prefix: str) -> None:
|
||||
"""Prepend a prefix to the agent framework user agent string.
|
||||
|
||||
This is useful for hosting layers that want to identify themselves in telemetry.
|
||||
Duplicate prefixes are ignored.
|
||||
|
||||
Args:
|
||||
prefix: The prefix to prepend (e.g. "foundry-hosting").
|
||||
"""
|
||||
if prefix and prefix not in _user_agent_prefixes:
|
||||
_user_agent_prefixes.append(prefix)
|
||||
|
||||
|
||||
def _get_user_agent() -> str:
|
||||
"""Return the full user agent string including any prepended prefixes."""
|
||||
if not _user_agent_prefixes:
|
||||
return AGENT_FRAMEWORK_USER_AGENT
|
||||
return f"{'/'.join(_user_agent_prefixes)}/{AGENT_FRAMEWORK_USER_AGENT}"
|
||||
|
||||
|
||||
def prepend_agent_framework_to_user_agent(headers: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
"""Prepend "agent-framework" to the User-Agent in the headers.
|
||||
@@ -57,12 +79,9 @@ def prepend_agent_framework_to_user_agent(headers: dict[str, Any] | None = None)
|
||||
"""
|
||||
if not IS_TELEMETRY_ENABLED:
|
||||
return headers or {}
|
||||
user_agent = _get_user_agent()
|
||||
if not headers:
|
||||
return {USER_AGENT_KEY: AGENT_FRAMEWORK_USER_AGENT}
|
||||
headers[USER_AGENT_KEY] = (
|
||||
f"{AGENT_FRAMEWORK_USER_AGENT} {headers[USER_AGENT_KEY]}"
|
||||
if USER_AGENT_KEY in headers
|
||||
else AGENT_FRAMEWORK_USER_AGENT
|
||||
)
|
||||
return {USER_AGENT_KEY: user_agent}
|
||||
headers[USER_AGENT_KEY] = f"{user_agent} {headers[USER_AGENT_KEY]}" if USER_AGENT_KEY in headers else user_agent
|
||||
|
||||
return headers
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,11 @@
|
||||
# Foundry Hosting
|
||||
|
||||
This package provides the integration of Agent Framework agents and workflows with the Foundry Agent Server, which can be hosted on Foundry infrastructure.
|
||||
|
||||
## Responses
|
||||
|
||||
TODO
|
||||
|
||||
## Invocations
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import importlib.metadata
|
||||
|
||||
from ._invocations import InvocationsHostServer
|
||||
from ._responses import ResponsesHostServer
|
||||
|
||||
try:
|
||||
__version__ = importlib.metadata.version(__name__)
|
||||
except importlib.metadata.PackageNotFoundError:
|
||||
__version__ = "0.0.0"
|
||||
|
||||
__all__ = ["InvocationsHostServer", "ResponsesHostServer"]
|
||||
@@ -0,0 +1,77 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from agent_framework import AgentSession, BaseAgent, SupportsAgentRun
|
||||
from agent_framework._telemetry import append_to_user_agent
|
||||
from azure.ai.agentserver.invocations import InvocationAgentServerHost
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse, Response, StreamingResponse
|
||||
from typing_extensions import Any, AsyncGenerator, Optional
|
||||
|
||||
|
||||
class InvocationsHostServer(InvocationAgentServerHost):
|
||||
"""An invocations server host for an agent."""
|
||||
|
||||
USER_AGENT_PREFIX = "foundry-hosting"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent: BaseAgent,
|
||||
*,
|
||||
stream: bool = False,
|
||||
openapi_spec: Optional[dict[str, Any]] = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize an InvocationsHostServer.
|
||||
|
||||
Args:
|
||||
agent: The agent to handle responses for.
|
||||
stream: Whether to stream the responses. Defaults to True.
|
||||
openapi_spec: The OpenAPI specification for the server.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
This host will expect the request to be a JSON body with a "message" field.
|
||||
The response from the host will be a JSON object with a "response" field containing
|
||||
the agent's response and a "session_id" field containing the session ID.
|
||||
"""
|
||||
super().__init__(openapi_spec=openapi_spec, **kwargs)
|
||||
|
||||
if not isinstance(agent, SupportsAgentRun):
|
||||
raise TypeError("Agent must support the SupportsAgentRun interface")
|
||||
|
||||
append_to_user_agent(self.USER_AGENT_PREFIX)
|
||||
self._agent = agent
|
||||
self._stream = stream
|
||||
self._sessions: dict[str, AgentSession] = {}
|
||||
self.invoke_handler(self._handle_invoke) # pyright: ignore[reportUnknownMemberType]
|
||||
|
||||
async def _handle_invoke(self, request: Request) -> Response:
|
||||
"""Invoke the agent with the given request."""
|
||||
data = await request.json()
|
||||
session_id: str = request.state.session_id
|
||||
|
||||
user_message = data.get("message", None)
|
||||
if user_message is None:
|
||||
error = "Missing 'message' in request"
|
||||
if self._stream:
|
||||
return StreamingResponse(content=error, status_code=400)
|
||||
return Response(content=error, status_code=400)
|
||||
|
||||
session = self._sessions.setdefault(session_id, AgentSession(session_id=session_id))
|
||||
|
||||
if self._stream:
|
||||
|
||||
async def stream_response() -> AsyncGenerator[str]:
|
||||
async for update in self._agent.run(user_message, session=session, stream=True):
|
||||
yield update.text
|
||||
|
||||
return StreamingResponse(
|
||||
stream_response(),
|
||||
media_type="text/event-stream",
|
||||
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"},
|
||||
)
|
||||
|
||||
response = await self._agent.run([user_message], session=session, stream=self._stream)
|
||||
return JSONResponse({
|
||||
"response": response.text,
|
||||
"session_id": session_id,
|
||||
})
|
||||
@@ -0,0 +1,304 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from collections.abc import AsyncIterable
|
||||
|
||||
from agent_framework import Agent, ChatOptions, Content, HistoryProvider, Message
|
||||
from agent_framework._telemetry import append_to_user_agent
|
||||
from azure.ai.agentserver.responses import (
|
||||
ResponseContext,
|
||||
ResponseEventStream,
|
||||
ResponseProviderProtocol,
|
||||
ResponsesServerOptions,
|
||||
)
|
||||
from azure.ai.agentserver.responses.hosting import ResponsesAgentServerHost
|
||||
from azure.ai.agentserver.responses.models import (
|
||||
ComputerScreenshotContent,
|
||||
CreateResponse,
|
||||
FunctionCallOutputItemParam,
|
||||
MessageContent,
|
||||
MessageContentInputFileContent,
|
||||
MessageContentInputImageContent,
|
||||
MessageContentInputTextContent,
|
||||
MessageContentOutputTextContent,
|
||||
MessageContentReasoningTextContent,
|
||||
MessageContentRefusalContent,
|
||||
OutputItem,
|
||||
OutputItemFunctionToolCall,
|
||||
OutputItemMessage,
|
||||
OutputItemOutputMessage,
|
||||
OutputItemReasoningItem,
|
||||
OutputMessageContent,
|
||||
OutputMessageContentOutputTextContent,
|
||||
OutputMessageContentRefusalContent,
|
||||
SummaryTextContent,
|
||||
TextContent,
|
||||
get_input_text,
|
||||
)
|
||||
from typing_extensions import Any, Sequence, cast
|
||||
|
||||
|
||||
class ResponsesHostServer(ResponsesAgentServerHost):
|
||||
"""A responses server host for an agent."""
|
||||
|
||||
USER_AGENT_PREFIX = "foundry-hosting"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
agent: Agent,
|
||||
*,
|
||||
prefix: str = "",
|
||||
options: ResponsesServerOptions | None = None,
|
||||
provider: ResponseProviderProtocol | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Initialize a ResponsesHostServer.
|
||||
|
||||
Args:
|
||||
agent: The agent to handle responses for.
|
||||
prefix: The URL prefix for the server.
|
||||
options: Optional server options.
|
||||
provider: Optional response provider.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Note:
|
||||
The agent must not have a history provider with `load_messages=True`,
|
||||
because history is managed by the hosting infrastructure.
|
||||
"""
|
||||
super().__init__(prefix=prefix, options=options, provider=provider, **kwargs)
|
||||
|
||||
self._validate_agent(agent)
|
||||
self._agent = agent
|
||||
self.create_handler(self._handle_create) # pyright: ignore[reportUnknownMemberType]
|
||||
|
||||
# Append the user agent prefix for telemetry purposes
|
||||
append_to_user_agent(self.USER_AGENT_PREFIX)
|
||||
|
||||
def _validate_agent(self, agent: Agent) -> None:
|
||||
"""Validate the agent to ensure it does not have a history provider with `load_messages=True`.
|
||||
|
||||
History is managed by the hosting infrastructure.
|
||||
"""
|
||||
for provider in agent.context_providers:
|
||||
if isinstance(provider, HistoryProvider) and provider.load_messages:
|
||||
raise RuntimeError(
|
||||
"There shouldn't be a history provider with `load_messages=True` already present. "
|
||||
"History is managed by the hosting infrastructure."
|
||||
)
|
||||
|
||||
async def _handle_create(
|
||||
self,
|
||||
request: CreateResponse,
|
||||
context: ResponseContext,
|
||||
cancellation_signal: asyncio.Event,
|
||||
) -> AsyncIterable[dict[str, Any]]:
|
||||
"""Handle the creation of a response."""
|
||||
input_items = get_input_text(request)
|
||||
history = await context.get_history()
|
||||
messages = [*_to_messages(history), input_items]
|
||||
|
||||
chat_options = _to_chat_options(request)
|
||||
|
||||
stream = ResponseEventStream(response_id=context.response_id, model=request.model)
|
||||
|
||||
yield stream.emit_created()
|
||||
yield stream.emit_in_progress()
|
||||
|
||||
# Add reasoning
|
||||
|
||||
if request.stream is None or request.stream is False:
|
||||
# Run the agent in non-streaming mode
|
||||
response = await self._agent.run(messages, stream=False, options=chat_options)
|
||||
for item in stream.output_item_message(response.text):
|
||||
yield item
|
||||
yield stream.emit_completed()
|
||||
return
|
||||
|
||||
# Start the streaming response
|
||||
message_item = stream.add_output_item_message()
|
||||
yield message_item.emit_added()
|
||||
text_content = message_item.add_text_content()
|
||||
yield text_content.emit_added()
|
||||
|
||||
# Invoke the MAF agent
|
||||
response_stream = self._agent.run(messages, stream=True, options=chat_options)
|
||||
async for update in response_stream:
|
||||
if update.text:
|
||||
yield text_content.emit_delta(update.text)
|
||||
|
||||
# Complete the message
|
||||
final = await response_stream.get_final_response()
|
||||
yield text_content.emit_done(final.text)
|
||||
yield message_item.emit_content_done(text_content)
|
||||
yield message_item.emit_done()
|
||||
|
||||
yield stream.emit_completed()
|
||||
|
||||
|
||||
# region Option Conversion
|
||||
|
||||
|
||||
def _to_chat_options(request: CreateResponse) -> ChatOptions:
|
||||
"""Converts a CreateResponse request to ChatOptions.
|
||||
|
||||
Args:
|
||||
request (CreateResponse): The request to convert.
|
||||
|
||||
Returns:
|
||||
ChatOptions: The converted ChatOptions.
|
||||
"""
|
||||
chat_options = ChatOptions()
|
||||
|
||||
if request.temperature is not None:
|
||||
chat_options["temperature"] = request.temperature
|
||||
if request.top_p is not None:
|
||||
chat_options["top_p"] = request.top_p
|
||||
if request.max_output_tokens is not None:
|
||||
chat_options["max_tokens"] = request.max_output_tokens
|
||||
if request.parallel_tool_calls is not None:
|
||||
chat_options["allow_multiple_tool_calls"] = request.parallel_tool_calls
|
||||
|
||||
return chat_options
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
|
||||
# region Message Conversion
|
||||
|
||||
|
||||
def _to_messages(history: Sequence[OutputItem]) -> list[Message]:
|
||||
"""Converts a sequence of OutputItem objects to a list of Message objects.
|
||||
|
||||
Args:
|
||||
history (Sequence[OutputItem]): The sequence of OutputItem objects to convert.
|
||||
|
||||
Returns:
|
||||
list[Message]: The list of Message objects.
|
||||
"""
|
||||
messages: list[Message] = []
|
||||
for item in history:
|
||||
messages.append(_to_message(item))
|
||||
return messages
|
||||
|
||||
|
||||
def _to_message(item: OutputItem) -> Message:
|
||||
"""Converts an OutputItem to a Message.
|
||||
|
||||
Args:
|
||||
item (OutputItem): The OutputItem to convert.
|
||||
|
||||
Returns:
|
||||
Message: The converted Message.
|
||||
|
||||
Raises:
|
||||
ValueError: If the OutputItem type is not supported.
|
||||
"""
|
||||
if item.type == "output_message":
|
||||
msg = cast(OutputItemOutputMessage, item)
|
||||
contents = [_convert_output_message_content(part) for part in msg.content]
|
||||
return Message(role=msg.role, contents=contents)
|
||||
|
||||
if item.type == "message":
|
||||
msg = cast(OutputItemMessage, item)
|
||||
contents = [_convert_message_content(part) for part in msg.content]
|
||||
return Message(role=msg.role, contents=contents)
|
||||
|
||||
if item.type == "function_call":
|
||||
fc = cast(OutputItemFunctionToolCall, item)
|
||||
return Message(
|
||||
role="assistant",
|
||||
contents=[Content.from_function_call(fc.call_id, fc.name, arguments=fc.arguments)],
|
||||
)
|
||||
|
||||
if item.type == "function_call_output":
|
||||
fco = cast(FunctionCallOutputItemParam, item)
|
||||
output = fco.output if isinstance(fco.output, str) else str(fco.output)
|
||||
return Message(
|
||||
role="tool",
|
||||
contents=[Content.from_function_result(fco.call_id, result=output)],
|
||||
)
|
||||
|
||||
if item.type == "reasoning":
|
||||
reasoning = cast(OutputItemReasoningItem, item)
|
||||
contents: list[Content] = []
|
||||
if reasoning.summary:
|
||||
for summary in reasoning.summary:
|
||||
contents.append(Content.from_text(summary.text))
|
||||
return Message(role="assistant", contents=contents)
|
||||
|
||||
raise ValueError(f"Unsupported OutputItem type: {item.type}")
|
||||
|
||||
|
||||
def _convert_output_message_content(content: OutputMessageContent) -> Content:
|
||||
"""Converts an OutputMessageContent to a Content object.
|
||||
|
||||
Args:
|
||||
content (OutputMessageContent): The OutputMessageContent to convert.
|
||||
|
||||
Returns:
|
||||
Content: The converted Content object.
|
||||
|
||||
Raises:
|
||||
ValueError: If the OutputMessageContent type is not supported.
|
||||
"""
|
||||
if content.type == "output_text":
|
||||
text_content = cast(OutputMessageContentOutputTextContent, content)
|
||||
return Content.from_text(text_content.text)
|
||||
if content.type == "refusal":
|
||||
refusal_content = cast(OutputMessageContentRefusalContent, content)
|
||||
return Content.from_text(refusal_content.refusal)
|
||||
|
||||
raise ValueError(f"Unsupported OutputMessageContent type: {content.type}")
|
||||
|
||||
|
||||
def _convert_message_content(content: MessageContent) -> Content:
|
||||
"""Converts a MessageContent to a Content object.
|
||||
|
||||
Args:
|
||||
content (MessageContent): The MessageContent to convert.
|
||||
|
||||
Returns:
|
||||
Content: The converted Content object.
|
||||
|
||||
Raises:
|
||||
ValueError: If the MessageContent type is not supported.
|
||||
"""
|
||||
if content.type == "input_text":
|
||||
input_text = cast(MessageContentInputTextContent, content)
|
||||
return Content.from_text(input_text.text)
|
||||
if content.type == "output_text":
|
||||
output_text = cast(MessageContentOutputTextContent, content)
|
||||
return Content.from_text(output_text.text)
|
||||
if content.type == "text":
|
||||
text = cast(TextContent, content)
|
||||
return Content.from_text(text.text)
|
||||
if content.type == "summary_text":
|
||||
summary = cast(SummaryTextContent, content)
|
||||
return Content.from_text(summary.text)
|
||||
if content.type == "refusal":
|
||||
refusal = cast(MessageContentRefusalContent, content)
|
||||
return Content.from_text(refusal.refusal)
|
||||
if content.type == "reasoning_text":
|
||||
reasoning = cast(MessageContentReasoningTextContent, content)
|
||||
return Content.from_text_reasoning(text=reasoning.text)
|
||||
if content.type == "input_image":
|
||||
image = cast(MessageContentInputImageContent, content)
|
||||
if image.image_url:
|
||||
return Content.from_uri(image.image_url)
|
||||
if image.file_id:
|
||||
return Content.from_hosted_file(image.file_id)
|
||||
if content.type == "input_file":
|
||||
file = cast(MessageContentInputFileContent, content)
|
||||
if file.file_url:
|
||||
return Content.from_uri(file.file_url)
|
||||
if file.file_id:
|
||||
return Content.from_hosted_file(file.file_id, name=file.filename)
|
||||
if content.type == "computer_screenshot":
|
||||
screenshot = cast(ComputerScreenshotContent, content)
|
||||
return Content.from_uri(screenshot.image_url)
|
||||
|
||||
raise ValueError(f"Unsupported MessageContent type: {content.type}")
|
||||
|
||||
|
||||
# endregion
|
||||
@@ -0,0 +1,104 @@
|
||||
[project]
|
||||
name = "agent-framework-foundry-hosting"
|
||||
description = "Foundry Hosting integration for Microsoft Agent Framework."
|
||||
authors = [{ name = "Microsoft", email = "af-support@microsoft.com"}]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
version = "1.0.0a260402"
|
||||
license-files = ["LICENSE"]
|
||||
urls.homepage = "https://aka.ms/agent-framework"
|
||||
urls.source = "https://github.com/microsoft/agent-framework/tree/main/python"
|
||||
urls.release_notes = "https://github.com/microsoft/agent-framework/releases?q=tag%3Apython-1&expanded=true"
|
||||
urls.issues = "https://github.com/microsoft/agent-framework/issues"
|
||||
classifiers = [
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Development Status :: 4 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
dependencies = [
|
||||
"agent-framework-core>=1.0.0,<2",
|
||||
"azure-ai-agentserver-core",
|
||||
"azure-ai-agentserver-responses",
|
||||
"azure-ai-agentserver-invocations"
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
azure-ai-agentserver-responses = { git = "https://github.com/Azure/azure-sdk-for-python.git", branch = "agentserver/responses", subdirectory = "sdk/agentserver/azure-ai-agentserver-responses" }
|
||||
azure-ai-agentserver-invocations = { git = "https://github.com/Azure/azure-sdk-for-python.git", branch = "agentserver/responses", subdirectory = "sdk/agentserver/azure-ai-agentserver-invocations" }
|
||||
azure-ai-agentserver-core = { git = "https://github.com/Azure/azure-sdk-for-python.git", branch = "agentserver/responses", subdirectory = "sdk/agentserver/azure-ai-agentserver-core" }
|
||||
|
||||
[tool.uv]
|
||||
prerelease = "if-necessary-or-explicit"
|
||||
environments = [
|
||||
"sys_platform == 'darwin'",
|
||||
"sys_platform == 'linux'",
|
||||
"sys_platform == 'win32'"
|
||||
]
|
||||
|
||||
[tool.uv-dynamic-versioning]
|
||||
fallback-version = "0.0.0"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = 'tests'
|
||||
addopts = "-ra -q -r fEX"
|
||||
asyncio_mode = "auto"
|
||||
asyncio_default_fixture_loop_scope = "function"
|
||||
filterwarnings = []
|
||||
timeout = 120
|
||||
markers = [
|
||||
"integration: marks tests as integration tests that require external services",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
extend = "../../pyproject.toml"
|
||||
|
||||
[tool.coverage.run]
|
||||
omit = [
|
||||
"**/__init__.py"
|
||||
]
|
||||
|
||||
[tool.pyright]
|
||||
extends = "../../pyproject.toml"
|
||||
include = ["agent_framework_foundry_hosting"]
|
||||
exclude = ['tests']
|
||||
|
||||
[tool.mypy]
|
||||
plugins = ['pydantic.mypy']
|
||||
strict = true
|
||||
python_version = "3.10"
|
||||
ignore_missing_imports = true
|
||||
disallow_untyped_defs = true
|
||||
no_implicit_optional = true
|
||||
check_untyped_defs = true
|
||||
warn_return_any = true
|
||||
show_error_codes = true
|
||||
warn_unused_ignores = false
|
||||
disallow_incomplete_defs = true
|
||||
disallow_untyped_decorators = true
|
||||
|
||||
[tool.bandit]
|
||||
targets = ["agent_framework_foundry_hosting"]
|
||||
exclude_dirs = ["tests"]
|
||||
|
||||
[tool.poe]
|
||||
executor.type = "uv"
|
||||
include = "../../shared_tasks.toml"
|
||||
|
||||
[tool.poe.tasks.mypy]
|
||||
help = "Run MyPy for this package."
|
||||
cmd = "mypy --config-file $POE_ROOT/pyproject.toml agent_framework_foundry_hosting"
|
||||
|
||||
[tool.poe.tasks.test]
|
||||
help = "Run the default unit test suite for this package."
|
||||
cmd = 'pytest -m "not integration" --cov=agent_framework_foundry_hosting --cov-report=term-missing:skip-covered tests'
|
||||
|
||||
[build-system]
|
||||
requires = ["flit-core >= 3.11,<4.0"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
Reference in New Issue
Block a user